1 /*
2  * Copyright (C) 2018, Matthias Clasen
3  *
4  * This file is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation, version 3.0 of the
7  * License.
8  *
9  * This file is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * SPDX-License-Identifier: LGPL-3.0-only
18  */
19 
20 #include "config.h"
21 
22 #include "inhibit.h"
23 #include "portal-private.h"
24 
25 typedef struct {
26   XdpPortal *portal;
27   XdpParent *parent;
28   char *parent_handle;
29   XdpInhibitFlags inhibit;
30   guint signal_id;
31   guint cancelled_id;
32   char *request_path;
33   char *reason;
34   GTask *task;
35   int id;
36 } InhibitCall;
37 
38 static void
inhibit_call_free(InhibitCall * call)39 inhibit_call_free (InhibitCall *call)
40 {
41   if (call->parent)
42     {
43       call->parent->parent_unexport (call->parent);
44       xdp_parent_free (call->parent);
45     }
46  g_free (call->parent_handle);
47 
48   if (call->signal_id)
49     g_dbus_connection_signal_unsubscribe (call->portal->bus, call->signal_id);
50 
51   if (call->cancelled_id)
52     g_signal_handler_disconnect (g_task_get_cancellable (call->task), call->cancelled_id);
53 
54   g_object_unref (call->portal);
55   g_object_unref (call->task);
56 
57   g_free (call->reason);
58   g_free (call->request_path);
59 
60   g_free (call);
61 }
62 
63 static void do_inhibit (InhibitCall *call);
64 
65 static void
inhibit_parent_exported(XdpParent * parent,const char * handle,gpointer data)66 inhibit_parent_exported (XdpParent *parent,
67                          const char *handle,
68                          gpointer data)
69 {
70   InhibitCall *call = data;
71   call->parent_handle = g_strdup (handle);
72   do_inhibit (call);
73 }
74 
75 static void
call_returned(GObject * object,GAsyncResult * result,gpointer data)76 call_returned (GObject *object,
77                GAsyncResult *result,
78                gpointer data)
79 {
80   InhibitCall *call = data;
81   GError *error = NULL;
82   g_autoptr(GVariant) ret = NULL;
83 
84   ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
85   if (error)
86     {
87       g_hash_table_remove (call->portal->inhibit_handles, GINT_TO_POINTER (call->id));
88       g_task_return_error (call->task, error);
89       inhibit_call_free (call);
90     }
91 }
92 
93 static void
response_received(GDBusConnection * bus,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer data)94 response_received (GDBusConnection *bus,
95                    const char *sender_name,
96                    const char *object_path,
97                    const char *interface_name,
98                    const char *signal_name,
99                    GVariant *parameters,
100                    gpointer data)
101 {
102   InhibitCall *call = data;
103   guint32 response;
104   g_autoptr(GVariant) ret = NULL;
105 
106   g_variant_get (parameters, "(u@a{sv})", &response, &ret);
107 
108   if (response == 0)
109     g_task_return_int (call->task, call->id);
110   else if (response == 1)
111     {
112       g_hash_table_remove (call->portal->inhibit_handles, GINT_TO_POINTER (call->id));
113       g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Account call canceled user");
114     }
115   else
116     {
117       g_hash_table_remove (call->portal->inhibit_handles, GINT_TO_POINTER (call->id));
118       g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "Account call failed");
119     }
120 
121   inhibit_call_free (call);
122 }
123 
124 static void
inhibit_cancelled_cb(GCancellable * cancellable,gpointer data)125 inhibit_cancelled_cb (GCancellable *cancellable,
126                       gpointer data)
127 {
128   InhibitCall *call = data;
129 
130 g_debug ("inhibit cancelled, calling Close");
131   g_dbus_connection_call (call->portal->bus,
132                           PORTAL_BUS_NAME,
133                           call->request_path,
134                           REQUEST_INTERFACE,
135                           "Close",
136                           NULL,
137                           NULL,
138                           G_DBUS_CALL_FLAGS_NONE,
139                           -1,
140                           NULL, NULL, NULL);
141 
142   g_hash_table_remove (call->portal->inhibit_handles, GINT_TO_POINTER (call->id));
143   g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Inhibit call canceled by caller");
144 
145   inhibit_call_free (call);
146 }
147 
148 static void
do_inhibit(InhibitCall * call)149 do_inhibit (InhibitCall *call)
150 {
151   GVariantBuilder options;
152   g_autofree char *token = NULL;
153   GCancellable *cancellable;
154 
155   if (call->parent_handle == NULL)
156     {
157       call->parent->parent_export (call->parent, inhibit_parent_exported, call);
158       return;
159     }
160 
161   token = g_strdup_printf ("portal%d", g_random_int_range (0, G_MAXINT));
162   call->request_path = g_strconcat (REQUEST_PATH_PREFIX, call->portal->sender, "/", token, NULL);
163   call->signal_id = g_dbus_connection_signal_subscribe (call->portal->bus,
164                                                         PORTAL_BUS_NAME,
165                                                         REQUEST_INTERFACE,
166                                                         "Response",
167                                                         call->request_path,
168                                                         NULL,
169                                                         G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
170                                                         response_received,
171                                                         call,
172                                                         NULL);
173 
174   g_hash_table_insert (call->portal->inhibit_handles, GINT_TO_POINTER (call->id), g_strdup (call->request_path));
175 
176   cancellable = g_task_get_cancellable (call->task);
177   if (cancellable)
178     call->cancelled_id = g_signal_connect (cancellable, "cancelled", G_CALLBACK (inhibit_cancelled_cb), call);
179 
180   g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
181   g_variant_builder_add (&options, "{sv}", "handle_token", g_variant_new_string (token));
182   g_variant_builder_add (&options, "{sv}", "reason", g_variant_new_string (call->reason));
183 
184   g_dbus_connection_call (call->portal->bus,
185                           PORTAL_BUS_NAME,
186                           PORTAL_OBJECT_PATH,
187                           "org.freedesktop.portal.Inhibit",
188                           "Inhibit",
189                           g_variant_new ("(sua{sv})", call->parent_handle, call->inhibit, &options),
190                           NULL,
191                           G_DBUS_CALL_FLAGS_NONE,
192                           -1,
193                           NULL,
194                           call_returned,
195                           call);
196 }
197 
198 /**
199  * xdp_portal_session_inhibit:
200  * @portal: a [class@Portal]
201  * @parent: (nullable): parent window information
202  * @reason: (nullable): user-visible reason for the inhibition
203  * @flags: information about what to inhibit
204  * @cancellable: (nullable): optional [class@Gio.Cancellable]
205  * @callback: (scope async): a callback to call when the request is done
206  * @data: (closure): data to pass to @callback
207  *
208  * Inhibits various session status changes.
209  *
210  * To obtain an ID that can be used to undo the inhibition, use
211  * [method@Portal.session_inhibit_finish] in the callback.
212  *
213  * To remove an active inhibitor, call [method@Portal.session_uninhibit]
214  * with the same ID.
215  */
216 void
xdp_portal_session_inhibit(XdpPortal * portal,XdpParent * parent,const char * reason,XdpInhibitFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer data)217 xdp_portal_session_inhibit (XdpPortal            *portal,
218                             XdpParent            *parent,
219                             const char           *reason,
220                             XdpInhibitFlags       flags,
221                             GCancellable         *cancellable,
222                             GAsyncReadyCallback   callback,
223                             gpointer              data)
224 {
225   InhibitCall *call = NULL;
226 
227   g_return_if_fail (XDP_IS_PORTAL (portal));
228   g_return_if_fail ((flags & ~(XDP_INHIBIT_FLAG_LOGOUT |
229                                XDP_INHIBIT_FLAG_USER_SWITCH |
230                                XDP_INHIBIT_FLAG_SUSPEND |
231                                XDP_INHIBIT_FLAG_IDLE)) == 0);
232 
233   if (portal->inhibit_handles == NULL)
234     portal->inhibit_handles = g_hash_table_new_full (NULL, NULL, NULL, g_free);
235 
236   portal->next_inhibit_id++;
237   if (portal->next_inhibit_id < 0)
238     portal->next_inhibit_id = 1;
239 
240   call = g_new0 (InhibitCall, 1);
241   call->portal = g_object_ref (portal);
242   if (parent)
243     call->parent = xdp_parent_copy (parent);
244   else
245     call->parent_handle = g_strdup ("");
246   call->inhibit = flags;
247   call->id = portal->next_inhibit_id;
248   call->reason = g_strdup (reason);
249   call->task = g_task_new (portal, cancellable, callback, data);
250   g_task_set_source_tag (call->task, xdp_portal_session_inhibit);
251 
252   do_inhibit (call);
253 }
254 
255 /**
256  * xdp_portal_session_inhibit_finish:
257  * @portal: a [class@Portal]
258  * @result: a [iface@Gio.AsyncResult]
259  * @error: return location for an error
260  *
261  * Finishes the inhbit request, and returns the ID of the
262  * inhibition as a positive integer. The ID can be passed to
263  * [method@Portal.session_uninhibit] to undo the inhibition.
264  *
265  * Returns: the ID of the inhibition, or -1 if there was an error
266  */
267 int
xdp_portal_session_inhibit_finish(XdpPortal * portal,GAsyncResult * result,GError ** error)268 xdp_portal_session_inhibit_finish (XdpPortal *portal,
269                                    GAsyncResult *result,
270                                    GError **error)
271 {
272   g_return_val_if_fail (XDP_IS_PORTAL (portal), -1);
273   g_return_val_if_fail (g_task_is_valid (result, portal), -1);
274   g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == xdp_portal_session_inhibit, -1);
275 
276   return g_task_propagate_int (G_TASK (result), error);
277 }
278 
279 /**
280  * xdp_portal_session_uninhibit:
281  * @portal: a [class@Portal]
282  * @id: unique ID for an active inhibition
283  *
284  * Removes an inhibitor that was created by a call
285  * to [method@Portal.session_inhibit].
286  */
287 void
xdp_portal_session_uninhibit(XdpPortal * portal,int id)288 xdp_portal_session_uninhibit (XdpPortal *portal,
289                               int        id)
290 {
291   gpointer key;
292   g_autofree char *value = NULL;
293 
294   g_return_if_fail (XDP_IS_PORTAL (portal));
295   g_return_if_fail (id > 0);
296 
297   if (portal->inhibit_handles == NULL ||
298       !g_hash_table_steal_extended (portal->inhibit_handles,
299                                     GINT_TO_POINTER (id),
300                                     (gpointer *)&key,
301                                     (gpointer *)&value))
302     {
303       g_warning ("No inhibit handle found");
304       return;
305     }
306 
307   g_dbus_connection_call (portal->bus,
308                           PORTAL_BUS_NAME,
309                           value,
310                           REQUEST_INTERFACE,
311                           "Close",
312                           g_variant_new ("()"),
313                           G_VARIANT_TYPE_UNIT,
314                           G_DBUS_CALL_FLAGS_NONE,
315                           G_MAXINT,
316                           NULL, NULL, NULL);
317 }
318 
319 typedef struct {
320   XdpPortal *portal;
321   XdpParent *parent;
322   char *parent_handle;
323   GTask *task;
324   char *request_path;
325   guint signal_id;
326   guint cancelled_id;
327   char *id;
328 } CreateMonitorCall;
329 
330 static void
create_monitor_call_free(CreateMonitorCall * call)331 create_monitor_call_free (CreateMonitorCall *call)
332 {
333   if (call->parent)
334     {
335       call->parent->parent_unexport (call->parent);
336       xdp_parent_free (call->parent);
337     }
338   g_free (call->parent_handle);
339 
340   if (call->signal_id)
341     g_dbus_connection_signal_unsubscribe (call->portal->bus, call->signal_id);
342 
343   if (call->cancelled_id)
344     g_signal_handler_disconnect (g_task_get_cancellable (call->task), call->cancelled_id);
345 
346   g_free (call->request_path);
347   g_free (call->id);
348 
349   g_object_unref (call->portal);
350   g_object_unref (call->task);
351 
352   g_free (call);
353 }
354 
355 static void
session_state_changed(GDBusConnection * bus,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer data)356 session_state_changed (GDBusConnection *bus,
357                        const char *sender_name,
358                        const char *object_path,
359                        const char *interface_name,
360                        const char *signal_name,
361                        GVariant *parameters,
362                        gpointer data)
363 {
364   XdpPortal *portal = data;
365   const char *handle;
366   g_autoptr(GVariant) state = NULL;
367   gboolean screensaver_active = FALSE;
368   XdpLoginSessionState session_state = XDP_LOGIN_SESSION_RUNNING;
369 
370   g_variant_get (parameters, "(&o@a{sv})", &handle, &state);
371   if (g_strcmp0 (handle, portal->session_monitor_handle) != 0)
372     {
373       g_warning ("Session monitor handle mismatch");
374       return;
375     }
376 
377   g_variant_lookup (state, "screensaver-active", "b", &screensaver_active);
378   g_variant_lookup (state, "session-state", "u", &session_state);
379 
380   g_signal_emit_by_name (portal, "session-state-changed",
381                          screensaver_active,
382                          session_state);
383 }
384 
385 static void
ensure_session_monitor_connection(XdpPortal * portal)386 ensure_session_monitor_connection (XdpPortal *portal)
387 {
388   if (portal->state_changed_signal == 0)
389     portal->state_changed_signal =
390        g_dbus_connection_signal_subscribe (portal->bus,
391                                            PORTAL_BUS_NAME,
392                                            "org.freedesktop.portal.Inhibit",
393                                            "StateChanged",
394                                            PORTAL_OBJECT_PATH,
395                                            NULL,
396                                            G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
397                                            session_state_changed,
398                                            portal,
399                                            NULL);
400 }
401 
402 static void
create_response_received(GDBusConnection * bus,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer data)403 create_response_received (GDBusConnection *bus,
404                           const char *sender_name,
405                           const char *object_path,
406                           const char *interface_name,
407                           const char *signal_name,
408                           GVariant *parameters,
409                           gpointer data)
410 {
411   CreateMonitorCall *call = data;
412   guint32 response;
413   g_autoptr(GVariant) ret = NULL;
414 
415   if (call->cancelled_id)
416     {
417       g_signal_handler_disconnect (g_task_get_cancellable (call->task), call->cancelled_id);
418       call->cancelled_id = 0;
419     }
420 
421   g_variant_get (parameters, "(u@a{sv})", &response, &ret);
422 
423   if (response == 0)
424     {
425       call->portal->session_monitor_handle = g_strdup (call->id);
426       ensure_session_monitor_connection (call->portal);
427       g_task_return_boolean (call->task, TRUE);
428     }
429   else if (response == 1)
430     g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "CreateMonitor canceled");
431   else
432     g_task_return_new_error (call->task, G_IO_ERROR, G_IO_ERROR_FAILED, "CreateMonitor failed");
433 
434   create_monitor_call_free (call);
435 }
436 
437 static void create_monitor (CreateMonitorCall *call);
438 
439 static void
create_parent_exported(XdpParent * parent,const char * handle,gpointer data)440 create_parent_exported (XdpParent *parent,
441                         const char *handle,
442                         gpointer data)
443 {
444   CreateMonitorCall *call = data;
445   call->parent_handle = g_strdup (handle);
446   create_monitor (call);
447 }
448 
449 static void
create_cancelled_cb(GCancellable * cancellable,gpointer data)450 create_cancelled_cb (GCancellable *cancellable,
451                      gpointer data)
452 {
453   CreateMonitorCall *call = data;
454 
455   g_dbus_connection_call (call->portal->bus,
456                           PORTAL_BUS_NAME,
457                           call->request_path,
458                           REQUEST_INTERFACE,
459                           "Close",
460                           NULL,
461                           NULL,
462                           G_DBUS_CALL_FLAGS_NONE,
463                           -1,
464                           NULL, NULL, NULL);
465 }
466 
467 static void
create_returned(GObject * object,GAsyncResult * result,gpointer data)468 create_returned (GObject *object,
469                  GAsyncResult *result,
470                  gpointer data)
471 {
472   CreateMonitorCall *call = data;
473   GError *error = NULL;
474   g_autoptr(GVariant) ret = NULL;
475 
476   ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (object), result, &error);
477   if (error)
478     {
479       g_task_return_error (call->task, error);
480       create_monitor_call_free (call);
481     }
482 }
483 
484 static void
create_monitor(CreateMonitorCall * call)485 create_monitor (CreateMonitorCall *call)
486 {
487   GVariantBuilder options;
488   g_autofree char *token = NULL;
489   g_autofree char *session_token = NULL;
490   GCancellable *cancellable;
491 
492   if (call->portal->session_monitor_handle)
493     {
494       g_task_return_boolean (call->task, TRUE);
495       create_monitor_call_free (call);
496       return;
497     }
498 
499   if (call->parent_handle == NULL)
500     {
501       call->parent->parent_export (call->parent, create_parent_exported, call);
502       return;
503     }
504 
505   token = g_strdup_printf ("portal%d", g_random_int_range (0, G_MAXINT));
506   call->request_path = g_strconcat (REQUEST_PATH_PREFIX, call->portal->sender, "/", token, NULL);
507   call->signal_id = g_dbus_connection_signal_subscribe (call->portal->bus,
508                                                         PORTAL_BUS_NAME,
509                                                         REQUEST_INTERFACE,
510                                                         "Response",
511                                                         call->request_path,
512                                                         NULL,
513                                                         G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
514                                                         create_response_received,
515                                                         call,
516                                                         NULL);
517 
518   cancellable = g_task_get_cancellable (call->task);
519   if (cancellable)
520     call->cancelled_id = g_signal_connect (cancellable, "cancelled", G_CALLBACK (create_cancelled_cb), call);
521 
522   session_token = g_strdup_printf ("portal%d", g_random_int_range (0, G_MAXINT));
523   call->id = g_strconcat (SESSION_PATH_PREFIX, call->portal->sender, "/", session_token, NULL);
524 
525   g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
526   g_variant_builder_add (&options, "{sv}", "handle_token", g_variant_new_string (token));
527   g_variant_builder_add (&options, "{sv}", "session_handle_token", g_variant_new_string (session_token));
528   g_dbus_connection_call (call->portal->bus,
529                           PORTAL_BUS_NAME,
530                           PORTAL_OBJECT_PATH,
531                           "org.freedesktop.portal.Inhibit",
532                           "CreateMonitor",
533                           g_variant_new ("(sa{sv})", call->parent_handle, &options),
534                           NULL,
535                           G_DBUS_CALL_FLAGS_NONE,
536                           -1,
537                           NULL,
538                           create_returned,
539                           call);
540 
541 }
542 
543 /**
544  * xdp_portal_session_monitor_start:
545  * @portal: a [class@Portal]
546  * @parent: (nullable): a XdpParent, or `NULL`
547  * @flags: options for this call
548  * @cancellable: (nullable): optional [class@Gio.Cancellable]
549  * @callback: (scope async): a callback to call when the request is done
550  * @data: (closure): data to pass to @callback
551  *
552  * Makes [class@Portal] start monitoring the login session state.
553  *
554  * When the state changes, the [signal@Portal::session-state-changed]
555  * signal is emitted.
556  *
557  * Use [method@Portal.session_monitor_stop] to stop monitoring.
558  */
559 void
xdp_portal_session_monitor_start(XdpPortal * portal,XdpParent * parent,XdpSessionMonitorFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer data)560 xdp_portal_session_monitor_start (XdpPortal *portal,
561                                   XdpParent *parent,
562                                   XdpSessionMonitorFlags flags,
563                                   GCancellable *cancellable,
564                                   GAsyncReadyCallback callback,
565                                   gpointer data)
566 
567 {
568   CreateMonitorCall *call;
569 
570   g_return_if_fail (XDP_IS_PORTAL (portal));
571   g_return_if_fail (flags == XDP_SESSION_MONITOR_FLAG_NONE);
572 
573   call = g_new0 (CreateMonitorCall, 1);
574   call->portal = g_object_ref (portal);
575   if (parent)
576     call->parent = xdp_parent_copy (parent);
577   else
578     call->parent_handle = g_strdup ("");
579   call->task = g_task_new (portal, cancellable, callback, data);
580   g_task_set_source_tag (call->task, xdp_portal_session_monitor_start);
581 
582   create_monitor (call);
583 }
584 
585 /**
586  * xdp_portal_session_monitor_start_finish:
587  * @portal: a [class@Portal]
588  * @result: a [iface@Gio.AsyncResult]
589  * @error: return location for an error
590  *
591  * Finishes a session-monitor request, and returns
592  * the result in the form of boolean.
593  *
594  * Returns: `TRUE` if the request succeeded
595  */
596 gboolean
xdp_portal_session_monitor_start_finish(XdpPortal * portal,GAsyncResult * result,GError ** error)597 xdp_portal_session_monitor_start_finish (XdpPortal *portal,
598                                          GAsyncResult *result,
599                                          GError **error)
600 {
601   g_return_val_if_fail (XDP_IS_PORTAL (portal), FALSE);
602   g_return_val_if_fail (g_task_is_valid (result, portal), FALSE);
603   g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == xdp_portal_session_monitor_start, FALSE);
604 
605   return g_task_propagate_boolean (G_TASK (result), error);
606 }
607 
608 /**
609  * xdp_portal_session_monitor_stop:
610  * @portal: a [class@Portal]
611  *
612  * Stops session state monitoring that was started with
613  * [method@Portal.session_monitor_start].
614  */
615 void
xdp_portal_session_monitor_stop(XdpPortal * portal)616 xdp_portal_session_monitor_stop (XdpPortal *portal)
617 {
618   g_return_if_fail (XDP_IS_PORTAL (portal));
619 
620   if (portal->state_changed_signal)
621     {
622       g_dbus_connection_signal_unsubscribe (portal->bus, portal->state_changed_signal);
623       portal->state_changed_signal = 0;
624     }
625 
626   if (portal->session_monitor_handle)
627     {
628       g_dbus_connection_call (portal->bus,
629                               PORTAL_BUS_NAME,
630                               portal->session_monitor_handle,
631                               SESSION_INTERFACE,
632                               "Close",
633                               NULL,
634                               NULL, 0, -1, NULL, NULL, NULL);
635       g_clear_pointer (&portal->session_monitor_handle, g_free);
636     }
637 }
638 
639 /**
640  * xdp_portal_session_monitor_query_end_response:
641  * @portal: a [class@Portal]
642  *
643  * This method should be called within one second of
644  * receiving a [signal@Portal::session-state-changed] signal
645  * with the 'Query End' state, to acknowledge that they
646  * have handled the state change.
647  *
648  * Possible ways to handle the state change are either
649  * to call [method@Portal.session_inhibit] to prevent the
650  * session from ending, or to save your state and get
651  * ready for the session to end.
652  */
653 void
xdp_portal_session_monitor_query_end_response(XdpPortal * portal)654 xdp_portal_session_monitor_query_end_response (XdpPortal *portal)
655 {
656   g_return_if_fail (XDP_IS_PORTAL (portal));
657 
658   if (portal->session_monitor_handle != NULL)
659     g_dbus_connection_call (portal->bus,
660                             PORTAL_BUS_NAME,
661                             PORTAL_OBJECT_PATH,
662                             "org.freedesktop.portal.Inhibit",
663                             "QueryEndResponse",
664                             g_variant_new ("(o)", portal->session_monitor_handle),
665                             NULL, 0, -1, NULL, NULL, NULL);
666 }
667