diff --git a/libportal/remote.c b/libportal/remote.c index ce77927e..3bdaec53 100644 --- a/libportal/remote.c +++ b/libportal/remote.c @@ -237,6 +237,12 @@ select_devices (CreateCall *call) g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&options, "{sv}", "handle_token", g_variant_new_string (token)); g_variant_builder_add (&options, "{sv}", "types", g_variant_new_uint32 (call->devices)); + if (call->portal->remote_desktop_interface_version >= 2) + { + g_variant_builder_add (&options, "{sv}", "persist_mode", g_variant_new_uint32 (call->persist_mode)); + if (call->restore_token) + g_variant_builder_add (&options, "{sv}", "restore_token", g_variant_new_string (call->restore_token)); + } g_dbus_connection_call (call->portal->bus, PORTAL_BUS_NAME, PORTAL_OBJECT_PATH, @@ -551,6 +557,48 @@ xdp_portal_create_remote_desktop_session (XdpPortal *portal, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) +{ + xdp_portal_create_remote_desktop_session_full (portal, + devices, + outputs, + flags, + cursor_mode, + XDP_PERSIST_MODE_NONE, + NULL, + cancellable, + callback, + data); +} + +/** + * xdp_portal_create_remote_desktop_session_full: + * @portal: a [class@Portal] + * @devices: which kinds of input devices to ofer in the new dialog + * @outputs: which kinds of source to offer in the dialog + * @flags: options for this call + * @cursor_mode: the cursor mode of the session + * @persist_mode: the persist mode of the session + * @restore_token: (nullable): the token of a previous screencast session to restore + * @cancellable: (nullable): optional [class@Gio.Cancellable] + * @callback: (scope async): a callback to call when the request is done + * @data: (closure): data to pass to @callback + * + * Creates a session for remote desktop. + * + * When the request is done, @callback will be called. You can then + * call [method@Portal.create_remote_desktop_session_finish] to get the results. + */ +void +xdp_portal_create_remote_desktop_session_full (XdpPortal *portal, + XdpDeviceType devices, + XdpOutputType outputs, + XdpRemoteDesktopFlags flags, + XdpCursorMode cursor_mode, + XdpPersistMode persist_mode, + const char *restore_token, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data) { CreateCall *call; @@ -563,8 +611,8 @@ xdp_portal_create_remote_desktop_session (XdpPortal *portal, call->devices = devices; call->outputs = outputs; call->cursor_mode = cursor_mode; - call->persist_mode = XDP_PERSIST_MODE_NONE; - call->restore_token = NULL; + call->persist_mode = persist_mode; + call->restore_token = g_strdup (restore_token); call->multiple = (flags & XDP_REMOTE_DESKTOP_FLAG_MULTIPLE) != 0; call->task = g_task_new (portal, cancellable, callback, data); @@ -1021,7 +1069,7 @@ xdp_session_pointer_motion (XdpSession *session, * * Moves the pointer to a new position in the given streams logical * coordinate space. - * + * * May only be called on a remote desktop session * with `XDP_DEVICE_POINTER` access. */ diff --git a/libportal/remote.h b/libportal/remote.h index a751861f..3ec90140 100644 --- a/libportal/remote.h +++ b/libportal/remote.h @@ -164,6 +164,19 @@ void xdp_portal_create_remote_desktop_session (XdpPortal GAsyncReadyCallback callback, gpointer data); +XDP_PUBLIC +void xdp_portal_create_remote_desktop_session_full (XdpPortal *portal, + XdpDeviceType devices, + XdpOutputType outputs, + XdpRemoteDesktopFlags flags, + XdpCursorMode cursor_mode, + XdpPersistMode persist_mode, + const char *restore_token, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data); + + XDP_PUBLIC XdpSession *xdp_portal_create_remote_desktop_session_finish (XdpPortal *portal, GAsyncResult *result, diff --git a/tests/pyportaltest/test_remotedesktop.py b/tests/pyportaltest/test_remotedesktop.py index 4250141b..ad28e31c 100644 --- a/tests/pyportaltest/test_remotedesktop.py +++ b/tests/pyportaltest/test_remotedesktop.py @@ -54,6 +54,8 @@ def create_session( flags=Xdp.RemoteDesktopFlags.NONE, cursor_mode=Xdp.CursorMode.HIDDEN, start_session=True, + persist_mode=None, + restore_token=None, ) -> SessionSetup: params = params or {} # To make the tests easier, load ScreenCast automatically if we have @@ -79,16 +81,28 @@ def create_session_done(portal, task, data): session_error = e self.mainloop.quit() - xdp.create_remote_desktop_session( - devices=devices, - outputs=outputs, - flags=flags, - cursor_mode=cursor_mode, - cancellable=cancellable, - callback=create_session_done, - data=None, - ) - + if restore_token is not None and persist_mode is not None: + xdp.create_remote_desktop_session_full( + devices=devices, + outputs=outputs, + flags=flags, + cursor_mode=cursor_mode, + persist_mode=persist_mode, + restore_token=restore_token, + cancellable=cancellable, + callback=create_session_done, + data=None, + ) + else: + xdp.create_remote_desktop_session( + devices=devices, + outputs=outputs, + flags=flags, + cursor_mode=cursor_mode, + cancellable=cancellable, + callback=create_session_done, + data=None, + ) self.mainloop.run() assert create_session_done_invoked if session_error is not None: @@ -166,8 +180,76 @@ def test_create_session(self): assert list(options.keys()) == [ "handle_token", "types", + "persist_mode", + ] + assert options["types"] == devices + assert options["persist_mode"] == Xdp.PersistMode.NONE + + method_calls = self.mock_interface.GetMethodCalls("SelectSources") + assert len(method_calls) == 1 + _, args = method_calls.pop(0) + session_handle, options = args + + assert list(options.keys()) == [ + "handle_token", + "types", + "multiple", + "cursor_mode", + "persist_mode", + ] + + assert options["types"] == outputs + assert options["multiple"] == flags + assert options["cursor_mode"] == cursor_mode + assert options["persist_mode"] == Xdp.PersistMode.NONE + + def test_create_session_restore(self): + """ + Create a session with some "random" values and ensure that they're + passed through to the portal. + """ + devices = Xdp.DeviceType.POINTER | Xdp.DeviceType.KEYBOARD + outputs = Xdp.OutputType.MONITOR | Xdp.OutputType.WINDOW + cursor_mode = Xdp.CursorMode.METADATA + flags = Xdp.RemoteDesktopFlags.MULTIPLE + persist_mode = Xdp.PersistMode.PERSISTENT + restore_token = "12345" + + self.create_session( + devices=devices, + outputs=outputs, + flags=flags, + cursor_mode=cursor_mode, + persist_mode=persist_mode, + restore_token=restore_token, + start_session=False, + ) + + # Now verify our DBus calls were correct + method_calls = self.mock_interface.GetMethodCalls("CreateSession") + assert len(method_calls) == 1 + _, args = method_calls.pop(0) + (options,) = args + + assert list(options.keys()) == [ + "handle_token", + "session_handle_token", + ] + + method_calls = self.mock_interface.GetMethodCalls("SelectDevices") + assert len(method_calls) == 1 + _, args = method_calls.pop(0) + session_handle, options = args + + assert list(options.keys()) == [ + "handle_token", + "types", + "persist_mode", + "restore_token", ] assert options["types"] == devices + assert options["persist_mode"] == persist_mode + assert options["restore_token"] == restore_token method_calls = self.mock_interface.GetMethodCalls("SelectSources") assert len(method_calls) == 1 @@ -180,6 +262,7 @@ def test_create_session(self): "multiple", "cursor_mode", "persist_mode", + "restore_token", ] assert options["types"] == outputs @@ -223,8 +306,10 @@ def test_create_session_no_outputs(self): assert list(options.keys()) == [ "handle_token", "types", + "persist_mode", ] assert options["types"] == devices + assert options["persist_mode"] == Xdp.PersistMode.NONE # No outputs means this should never get called method_calls = self.mock_interface.GetMethodCalls("SelectSources")