diff --git a/configure.ac b/configure.ac
index 2ac444d76c..a626d15284 100644
--- a/configure.ac
+++ b/configure.ac
@@ -346,6 +346,9 @@ I can find them.
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
+PKG_CHECK_MODULES(GIO_UNIX, [gio-unix-2.0 >= 2.26],
+			    AC_DEFINE(HAVE_GIOUNIX, 1, [Have gio-unix]), [])
+
 GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
 AC_SUBST(GLIB_GENMARSHAL)
 
diff --git a/pidgin/Makefile.am b/pidgin/Makefile.am
index 2b9f23c6c7..3b72a5fdba 100644
--- a/pidgin/Makefile.am
+++ b/pidgin/Makefile.am
@@ -161,6 +161,7 @@ pidgin_LDADD = \
 	$(GTKSPELL_LIBS) \
 	$(LIBXML_LIBS) \
 	$(GTK_LIBS) \
+	$(GIO_UNIX_LIBS) \
 	$(top_builddir)/libpurple/libpurple.la
 
 AM_CPPFLAGS = \
@@ -176,6 +177,7 @@ AM_CPPFLAGS = \
 	$(GSTREAMER_CFLAGS) \
 	$(DEBUG_CFLAGS) \
 	$(GTK_CFLAGS) \
+	$(GIO_UNIX_CFLAGS) \
 	$(DBUS_CFLAGS) \
 	$(GTKSPELL_CFLAGS) \
 	$(LIBXML_CFLAGS) \
diff --git a/pidgin/gtkrequest.c b/pidgin/gtkrequest.c
index 6aba4dddb6..db6a444833 100644
--- a/pidgin/gtkrequest.c
+++ b/pidgin/gtkrequest.c
@@ -80,6 +80,16 @@ typedef struct
 
 		} file;
 
+		struct
+		{
+			GCancellable *cancellable;
+			GDBusConnection *dbus_connection;
+			gchar *session_path;
+			guint signal_id;
+			guint32 node_id;
+
+		} screenshare;
+
 	} u;
 
 } PidginRequestData;
@@ -1707,6 +1717,331 @@ pidgin_request_folder(const char *title, const char *dirname,
 }
 
 #ifdef USE_VV
+
+#ifdef HAVE_GIOUNIX
+#include <gio/gunixfdlist.h>
+
+static gboolean portal_failed;
+
+static void screenshare_cancel_cb(GtkWidget *button, PidginRequestData *data);
+
+static void portal_cancel_cb(PidginRequestData *data)
+{
+	screenshare_cancel_cb(NULL, data);
+}
+
+static void portal_fallback(PidginRequestData *data)
+{
+	portal_failed = TRUE;
+
+	// XXX fall back this time, not next!
+	portal_cancel_cb(data);
+}
+
+static void request_completed_cb(GObject *object, GAsyncResult *res, gpointer _data)
+{
+	PidginRequestData *data = _data;
+	GError *error = NULL;
+	GDBusMessage *msg = g_dbus_connection_send_message_with_reply_finish(data->u.screenshare.dbus_connection,
+									     res,
+									     &error);
+	if (!msg || g_dbus_message_to_gerror(msg, &error)) {
+		printf("msg failed: %s\n", error->message);
+		g_clear_error(&error);
+		portal_fallback(data);
+	}
+}
+
+
+static guint portal_session_nr, portal_request_nr;
+
+static gchar *portal_session_path(GDBusConnection *dc, GVariantBuilder *b)
+{
+	const gchar *bus_name = g_dbus_connection_get_unique_name(dc);
+	gchar *dot, *session_str = g_strdup_printf("u%u", portal_session_nr++);
+	gchar *session_path = g_strdup_printf("/org/freedesktop/portal/desktop/session/%s/%s",
+					      bus_name + 1, session_str);
+
+	g_variant_builder_add(b, "{sv}", "session_handle_token", g_variant_new_take_string(session_str));
+
+	dot = session_path;
+	while ((dot = strchr(dot, '.')))
+		*dot = '_';
+
+	return session_path;
+}
+
+static gchar *portal_request_path(GDBusConnection *dc, GVariantBuilder *b)
+{
+	const gchar *bus_name = g_dbus_connection_get_unique_name(dc);
+	gchar *dot, *request_str = g_strdup_printf("u%u", portal_request_nr++);
+	gchar *request_path = g_strdup_printf("/org/freedesktop/portal/desktop/request/%s/%s",
+					      bus_name + 1, request_str);
+
+	g_variant_builder_add(b, "{sv}", "handle_token", g_variant_new_take_string(request_str));
+
+	dot = request_path;
+	while ((dot = strchr(dot, '.')))
+		*dot = '_';
+
+	return request_path;
+}
+
+static void screen_cast_call(PidginRequestData *data, const gchar *method,
+			     const gchar *obj_arg1, const gchar *str_arg1,
+			     GVariantBuilder *opts, GDBusSignalCallback cb)
+{
+	GDBusMessage *msg;
+	GVariant *args;
+	gchar *request_path;
+
+	if (data->u.screenshare.signal_id)
+		g_dbus_connection_signal_unsubscribe(data->u.screenshare.dbus_connection,
+						     data->u.screenshare.signal_id);
+
+	if (!opts)
+		opts = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+	request_path = portal_request_path(data->u.screenshare.dbus_connection, opts);
+
+	data->u.screenshare.signal_id = g_dbus_connection_signal_subscribe(data->u.screenshare.dbus_connection,
+									   "org.freedesktop.portal.Desktop",
+									   "org.freedesktop.portal.Request",
+									   "Response", request_path, NULL, 0,
+									   cb, data, NULL);
+	g_free(request_path);
+
+	msg = g_dbus_message_new_method_call("org.freedesktop.portal.Desktop",
+					     "/org/freedesktop/portal/desktop",
+					     "org.freedesktop.portal.ScreenCast",
+					     method);
+
+	if (str_arg1)
+		args = g_variant_new("(osa{sv})", obj_arg1, str_arg1, opts);
+	else if (obj_arg1)
+		args = g_variant_new("(oa{sv})", obj_arg1, opts);
+	else
+		args = g_variant_new("(a{sv})", opts);
+
+	g_dbus_message_set_body(msg, args);
+
+	g_dbus_connection_send_message_with_reply(data->u.screenshare.dbus_connection, msg,
+						  0, 200, NULL, NULL, request_completed_cb, data);
+}
+
+static guint32 portal_response(GVariant *resp, GVariant **params)
+{
+	GVariantIter iter;
+	GVariant *code;
+	guint32 ret;
+
+	if (!g_variant_is_container(resp))
+		return -1;
+
+	g_variant_iter_init(&iter, resp);
+	code = g_variant_iter_next_value(&iter);
+	if (!code || !g_variant_is_of_type(code, G_VARIANT_TYPE_UINT32))
+		return -1;
+
+	ret = g_variant_get_uint32(code);
+	if (params)
+		*params = g_variant_iter_next_value(&iter);
+
+	return ret;
+}
+static GstElement *create_pipewiresrc_cb(PurpleMedia *media, const gchar *session_id,
+					 const gchar *participant)
+{
+	GObject *info;
+	GstElement *ret;
+	gchar *node_id;
+
+	info = g_object_get_data(G_OBJECT(media), "src-element");
+	if (!info)
+		return NULL;
+
+	ret = gst_element_factory_make("pipewiresrc", NULL);
+	if (ret == NULL)
+		return NULL;
+
+	node_id = g_strdup_printf("%u", GPOINTER_TO_UINT(g_object_get_data(info, "node-id")));
+	g_object_set(ret, "fd", GPOINTER_TO_UINT(g_object_get_data(info, "fd")),
+		     "path", node_id, "do-timestamp", TRUE, NULL);
+	g_free(node_id);
+
+	return ret;
+}
+
+static void pipewire_fd_cb(GObject *object, GAsyncResult *res, gpointer _data)
+{
+	PidginRequestData *data = _data;
+	GError *error = NULL;
+	GUnixFDList *l;
+	int pipewire_fd;
+	GDBusMessage *msg = g_dbus_connection_send_message_with_reply_finish(data->u.screenshare.dbus_connection,
+									     res,
+									     &error);
+	if (!msg || g_dbus_message_to_gerror(msg, &error)) {
+		printf("msg failed: %s\n", error->message);
+		g_clear_error(&error);
+		portal_fallback(data);
+	}
+	l = g_dbus_message_get_unix_fd_list(msg);
+	if (!l)
+		portal_fallback(data);
+	pipewire_fd = g_unix_fd_list_get(l, 0, NULL);
+
+	if (data->cbs[0] != NULL) {
+		GObject *info;
+		info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
+				    "id", "screenshare-window",
+				    "name", "Screen share single window",
+				    "type", PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC |
+				    PURPLE_MEDIA_ELEMENT_ONE_SRC,
+				    "create-cb", create_pipewiresrc_cb, NULL);
+		g_object_set_data_full(info, "node-id", GUINT_TO_POINTER(data->u.screenshare.node_id), (GDestroyNotify)close);
+		g_object_set_data(info, "fd", GUINT_TO_POINTER(pipewire_fd));
+		((PurpleRequestScreenshareCb)data->cbs[0])(data->user_data, info);
+	}
+
+	printf("got pipewire fd %d\n", pipewire_fd);
+}
+
+
+static void get_pipewire_fd(PidginRequestData *data)
+{
+	GDBusMessage *msg;
+	GVariant *args;
+
+	if (data->u.screenshare.signal_id)
+		g_dbus_connection_signal_unsubscribe(data->u.screenshare.dbus_connection,
+						     data->u.screenshare.signal_id);
+	data->u.screenshare.signal_id = 0;
+
+	msg = g_dbus_message_new_method_call("org.freedesktop.portal.Desktop",
+					     "/org/freedesktop/portal/desktop",
+					     "org.freedesktop.portal.ScreenCast",
+					     "OpenPipeWireRemote");
+
+	args = g_variant_new("(oa{sv})", data->u.screenshare.session_path, NULL);
+	g_dbus_message_set_body(msg, args);
+
+	g_dbus_connection_send_message_with_reply(data->u.screenshare.dbus_connection, msg,
+						  0, 200, NULL, NULL, pipewire_fd_cb, data);
+}
+
+static void started_cb(GDBusConnection *dc, const gchar *sender_name,
+		       const gchar *object_path, const gchar *interface_name,
+		       const gchar *signal_name, GVariant *params, gpointer _data)
+{
+	PidginRequestData *data = _data;
+	GVariantDict dict;
+	GVariantIter iter;
+	GVariant *dict_v, *streams, *stream, *id_v;
+
+	printf("signal %s indicated %d: %s\n", signal_name,
+	       portal_response(params, NULL), g_variant_print(params, TRUE));
+
+	if (portal_response(params, &dict_v)) {
+		portal_cancel_cb(data);
+		return;
+	}
+
+	// XX: Be more defensive
+	g_variant_dict_init(&dict, dict_v);
+	streams = g_variant_dict_lookup_value(&dict, "streams", G_VARIANT_TYPE("a(ua{sv})"));
+	g_variant_dict_clear(&dict);
+	if (!streams) {
+		printf("no streams\n");
+		portal_cancel_cb(data);
+		return;
+	}
+
+	g_variant_iter_init(&iter, streams);
+	stream = g_variant_iter_next_value(&iter);
+	if (!stream) {
+		printf("empty streams\n");
+		portal_cancel_cb(data);
+		return;
+	}
+	g_variant_iter_init(&iter, stream);
+	id_v = g_variant_iter_next_value(&iter);
+	if (!id_v || !g_variant_is_of_type(id_v, G_VARIANT_TYPE_UINT32)) {
+		printf("broken stream\n");
+		portal_fallback(data);
+		return;
+	}
+	data->u.screenshare.node_id = g_variant_get_uint32(id_v);
+
+	get_pipewire_fd(data);
+}
+
+static void source_selected_cb(GDBusConnection *dc, const gchar *sender_name,
+			       const gchar *object_path, const gchar *interface_name,
+			       const gchar *signal_name, GVariant *params, gpointer _data)
+{
+	PidginRequestData *data = _data;
+
+	if (portal_response(params, NULL)) {
+		portal_fallback(data);
+		return;
+	}
+
+	screen_cast_call(data, "Start", data->u.screenshare.session_path,
+			 "", NULL, started_cb);
+}
+
+static void sess_created_cb(GDBusConnection *dc, const gchar *sender_name,
+			    const gchar *object_path, const gchar *interface_name,
+			    const gchar *signal_name, GVariant *params, gpointer _data)
+{
+	PidginRequestData *data = _data;
+	GVariantBuilder *opts;
+
+	if (portal_response(params, NULL)) {
+		portal_fallback(data);
+		return;
+	}
+
+	opts = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+	g_variant_builder_add(opts, "{sv}", "multiple", g_variant_new_boolean(FALSE));
+	g_variant_builder_add(opts, "{sv}", "types", g_variant_new_uint32(3));
+
+	screen_cast_call(data, "SelectSources",
+			 data->u.screenshare.session_path, NULL,
+			 opts, source_selected_cb);
+}
+
+static void portal_conn_cb(GObject *object, GAsyncResult *res, gpointer _data)
+{
+	PidginRequestData *data = _data;
+	GVariantBuilder *opts;
+
+	data->u.screenshare.dbus_connection = g_bus_get_finish(res, NULL);
+	if (!data->u.screenshare.dbus_connection) {
+		portal_fallback(data);
+		return;
+	}
+
+	opts = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+	data->u.screenshare.session_path = portal_session_path(data->u.screenshare.dbus_connection, opts);
+
+	screen_cast_call(data, "CreateSession", NULL, NULL,
+			 opts, sess_created_cb);
+}
+
+static gboolean request_xdp_portal_screenshare(PidginRequestData *data)
+{
+	if (portal_failed)
+		return FALSE;
+
+	data->u.screenshare.cancellable = g_cancellable_new();
+
+	g_bus_get(G_BUS_TYPE_SESSION, NULL, portal_conn_cb, data);
+	return TRUE;
+}
+
+#endif
+
 static GstElement *create_screensrc_cb(PurpleMedia *media, const gchar *session_id,
 				       const gchar *participant);
 
@@ -1911,7 +2246,8 @@ screenshare_videotest_cb(GtkWidget *button, PidginRequestData *data)
 static void
 screenshare_cancel_cb(GtkWidget *button, PidginRequestData *data)
 {
-	generic_response_start(data);
+	if (data->dialog)
+		generic_response_start(data);
 
 	if (data->cbs[0] != NULL)
 		((PurpleRequestScreenshareCb)data->cbs[0])(data->user_data, NULL);
@@ -1950,6 +2286,11 @@ static void *pidgin_request_screenshare_media(const char *title, const char *pri
 	data->cbs = g_new0(GCallback, 1);
 	data->cbs[0] = cb;
 
+#ifdef HAVE_GIOUNIX
+	if (request_xdp_portal_screenshare(data))
+		return data;
+#endif
+
 	/* Create the dialog. */
 	data->dialog = dialog = gtk_dialog_new();
 
@@ -2072,12 +2413,22 @@ pidgin_close_request(PurpleRequestType type, void *ui_handle)
 
 	g_free(data->cbs);
 
-	gtk_widget_destroy(data->dialog);
+	if (data->dialog)
+		gtk_widget_destroy(data->dialog);
 
 	if (type == PURPLE_REQUEST_FIELDS)
 		purple_request_fields_destroy(data->u.multifield.fields);
 	else if (type == PURPLE_REQUEST_FILE)
 		g_free(data->u.file.name);
+	else if (type == PURPLE_REQUEST_SCREENSHARE) {
+		g_cancellable_cancel(data->u.screenshare.cancellable);
+		if (data->u.screenshare.signal_id)
+			g_dbus_connection_signal_unsubscribe(data->u.screenshare.dbus_connection,
+							     data->u.screenshare.signal_id);
+		g_clear_object(&data->u.screenshare.dbus_connection);
+		g_free(data->u.screenshare.session_path);
+		g_clear_object(&data->u.screenshare.cancellable);
+	}
 
 	g_free(data);
 }
