From 0a9c097f04cca83080a9ca4f9a3567d95e708e64 Mon Sep 17 00:00:00 2001 From: Jan Grulich Date: Tue, 9 Jan 2024 15:24:07 +0100 Subject: [PATCH] Add screencast stream restoration support --- .xdg-desktop-portal-gnome.metadata | 1 + 0001-screencast-trivial-style-cleanups.patch | 638 +++++++++++++++ ...icate-monitor-and-window-stream-info.patch | 223 ++++++ ...anup-unnecessary-forward-declaration.patch | 23 + ...ast-more-code-shuffling-and-cleanups.patch | 452 +++++++++++ ...creencastwidget-rework-selection-api.patch | 744 ++++++++++++++++++ ...displaystatetracker-add-match-string.patch | 90 +++ ...lement-screencast-stream-restoration.patch | 724 +++++++++++++++++ 0007-screencast-pass-stream-ids.patch | 166 ++++ 0008-screencast-fix-variant-type-check.patch | 24 + ...re-windows-with-too-different-titles.patch | 30 + ...wrap-restore-data-in-another-variant.patch | 29 + ...fix-window-screencast-stream-restore.patch | 390 +++++++++ ...e-session-is-closed-from-client-side.patch | 27 + ...-when-we-are-closing-our-own-account.patch | 87 ++ xdg-desktop-portal-gnome.spec | 33 +- 16 files changed, 3680 insertions(+), 1 deletion(-) create mode 100644 .xdg-desktop-portal-gnome.metadata create mode 100644 0001-screencast-trivial-style-cleanups.patch create mode 100644 00014-screencasts-duplicate-monitor-and-window-stream-info.patch create mode 100644 0002-screencast-cleanup-unnecessary-forward-declaration.patch create mode 100644 0003-screencast-more-code-shuffling-and-cleanups.patch create mode 100644 0004-screencastwidget-rework-selection-api.patch create mode 100644 0005-displaystatetracker-add-match-string.patch create mode 100644 0006-implement-screencast-stream-restoration.patch create mode 100644 0007-screencast-pass-stream-ids.patch create mode 100644 0008-screencast-fix-variant-type-check.patch create mode 100644 0009-screencast-dont-restore-windows-with-too-different-titles.patch create mode 100644 0010-screencast-dont-wrap-restore-data-in-another-variant.patch create mode 100644 0011-fix-window-screencast-stream-restore.patch create mode 100644 0012-screencast-close-screencast-dialog-when-the-session-is-closed-from-client-side.patch create mode 100644 0013-session-emit-closed-signal-when-we-are-closing-our-own-account.patch diff --git a/.xdg-desktop-portal-gnome.metadata b/.xdg-desktop-portal-gnome.metadata new file mode 100644 index 0000000..a847940 --- /dev/null +++ b/.xdg-desktop-portal-gnome.metadata @@ -0,0 +1 @@ +0ff21a277478d553fcb740c7cd01f28c8d932997 xdg-desktop-portal-gnome-41.2.tar.xz diff --git a/0001-screencast-trivial-style-cleanups.patch b/0001-screencast-trivial-style-cleanups.patch new file mode 100644 index 0000000..a253189 --- /dev/null +++ b/0001-screencast-trivial-style-cleanups.patch @@ -0,0 +1,638 @@ +From d9c897c8b91165f95f011d44f2fea731b4b7eb16 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Wed, 10 Nov 2021 15:32:18 -0300 +Subject: [PATCH] screencast: Trivial style cleanups + +Move some code around, and cleanup some of the styling. +It's getting increasingly harder to follow these files, +so start this series of cleanups with them. +--- + src/screencast.c | 30 ++-- + src/screencastdialog.c | 62 +++---- + src/screencastdialog.h | 2 +- + src/screencastwidget.c | 356 +++++++++++++++++++++-------------------- + src/screencastwidget.h | 10 +- + 5 files changed, 238 insertions(+), 222 deletions(-) + +diff --git a/src/gnomescreencast.c b/src/gnomescreencast.c +index fd80ca7..abf5b53 100644 +--- a/src/gnomescreencast.c ++++ b/src/gnomescreencast.c +@@ -473,6 +473,7 @@ gnome_screen_cast_session_record_selections (GnomeScreenCastSession *gnome_scree + select, + error)) + return FALSE; ++ break; + } + } + +diff --git a/src/screencast.c b/src/screencast.c +index 27d8946..38d1d05 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -572,21 +572,6 @@ on_gnome_screen_cast_disabled (GDBusConnection *connection, + } + } + +-gboolean +-screen_cast_init (GDBusConnection *connection, +- GError **error) +-{ +- impl_connection = connection; +- gnome_screen_cast = gnome_screen_cast_new (connection); +- +- g_signal_connect (gnome_screen_cast, "enabled", +- G_CALLBACK (on_gnome_screen_cast_enabled), NULL); +- g_signal_connect (gnome_screen_cast, "disabled", +- G_CALLBACK (on_gnome_screen_cast_disabled), NULL); +- +- return TRUE; +-} +- + static void + screen_cast_session_close (Session *session) + { +@@ -636,3 +621,18 @@ screen_cast_session_class_init (ScreenCastSessionClass *klass) + session_class = (SessionClass *)klass; + session_class->close = screen_cast_session_close; + } ++ ++gboolean ++screen_cast_init (GDBusConnection *connection, ++ GError **error) ++{ ++ impl_connection = connection; ++ gnome_screen_cast = gnome_screen_cast_new (connection); ++ ++ g_signal_connect (gnome_screen_cast, "enabled", ++ G_CALLBACK (on_gnome_screen_cast_enabled), NULL); ++ g_signal_connect (gnome_screen_cast, "disabled", ++ G_CALLBACK (on_gnome_screen_cast_disabled), NULL); ++ ++ return TRUE; ++} +diff --git a/src/screencastdialog.c b/src/screencastdialog.c +index 306992d..56d4d49 100644 +--- a/src/screencastdialog.c ++++ b/src/screencastdialog.c +@@ -51,6 +51,10 @@ static guint signals[N_SIGNALS]; + + G_DEFINE_TYPE (ScreenCastDialog, screen_cast_dialog, GTK_TYPE_WINDOW) + ++/* ++ * Callbacks ++ */ ++ + static void + button_clicked (GtkWidget *button, + ScreenCastDialog *dialog) +@@ -84,7 +88,7 @@ button_clicked (GtkWidget *button, + + static void + on_has_selection_changed (ScreenCastWidget *screen_cast_widget, +- gboolean has_selection, ++ gboolean has_selection, + ScreenCastDialog *dialog) + { + if (has_selection) +@@ -93,32 +97,9 @@ on_has_selection_changed (ScreenCastWidget *screen_cast_widget, + gtk_widget_set_sensitive (dialog->accept_button, FALSE); + } + +-ScreenCastDialog * +-screen_cast_dialog_new (const char *app_id, +- ScreenCastSelection *select) +-{ +- ScreenCastDialog *dialog; +- ScreenCastWidget *screen_cast_widget; +- +- dialog = g_object_new (SCREEN_CAST_TYPE_DIALOG, NULL); +- screen_cast_widget = SCREEN_CAST_WIDGET (dialog->screen_cast_widget); +- screen_cast_widget_set_app_id (screen_cast_widget, app_id); +- screen_cast_widget_set_allow_multiple (screen_cast_widget, select->multiple); +- screen_cast_widget_set_source_types (screen_cast_widget, +- select->source_types); +- +- return dialog; +-} +- +-static void +-screen_cast_dialog_init (ScreenCastDialog *dialog) +-{ +- gtk_widget_init_template (GTK_WIDGET (dialog)); +- +- g_signal_connect (dialog->screen_cast_widget, "has-selection-changed", +- G_CALLBACK (on_has_selection_changed), dialog); +- gtk_widget_show (dialog->screen_cast_widget); +-} ++/* ++ * GtkWindow overrides ++ */ + + static gboolean + screen_cast_dialog_close_request (GtkWindow *dialog) +@@ -155,3 +136,30 @@ screen_cast_dialog_class_init (ScreenCastDialogClass *klass) + gtk_widget_class_bind_template_child (widget_class, ScreenCastDialog, screen_cast_widget); + gtk_widget_class_bind_template_callback (widget_class, button_clicked); + } ++ ++static void ++screen_cast_dialog_init (ScreenCastDialog *dialog) ++{ ++ gtk_widget_init_template (GTK_WIDGET (dialog)); ++ ++ g_signal_connect (dialog->screen_cast_widget, "has-selection-changed", ++ G_CALLBACK (on_has_selection_changed), dialog); ++ gtk_widget_show (dialog->screen_cast_widget); ++} ++ ++ScreenCastDialog * ++screen_cast_dialog_new (const char *app_id, ++ ScreenCastSelection *select) ++{ ++ ScreenCastDialog *dialog; ++ ScreenCastWidget *screen_cast_widget; ++ ++ dialog = g_object_new (SCREEN_CAST_TYPE_DIALOG, NULL); ++ screen_cast_widget = SCREEN_CAST_WIDGET (dialog->screen_cast_widget); ++ screen_cast_widget_set_app_id (screen_cast_widget, app_id); ++ screen_cast_widget_set_allow_multiple (screen_cast_widget, select->multiple); ++ screen_cast_widget_set_source_types (screen_cast_widget, ++ select->source_types); ++ ++ return dialog; ++} +diff --git a/src/screencastdialog.h b/src/screencastdialog.h +index 247d93b..1fca470 100644 +--- a/src/screencastdialog.h ++++ b/src/screencastdialog.h +@@ -26,5 +26,5 @@ + G_DECLARE_FINAL_TYPE (ScreenCastDialog, screen_cast_dialog, + SCREEN_CAST, DIALOG, GtkWindow) + +-ScreenCastDialog * screen_cast_dialog_new (const char *app_id, ++ScreenCastDialog * screen_cast_dialog_new (const char *app_id, + ScreenCastSelection *select); +diff --git a/src/screencastwidget.c b/src/screencastwidget.c +index cdd57dc..454c93e 100644 +--- a/src/screencastwidget.c ++++ b/src/screencastwidget.c +@@ -65,6 +65,55 @@ static GQuark quark_window_widget_data; + + G_DEFINE_TYPE (ScreenCastWidget, screen_cast_widget, GTK_TYPE_BOX) + ++/* ++ * Auxiliary methods ++ */ ++ ++static gboolean ++add_selections (ScreenCastWidget *widget, ++ GVariantBuilder *source_selections_builder) ++{ ++ GList *selected_monitor_rows; ++ GList *selected_window_rows; ++ GList *l; ++ ++ selected_monitor_rows = ++ gtk_list_box_get_selected_rows (GTK_LIST_BOX (widget->monitor_list)); ++ selected_window_rows = ++ gtk_list_box_get_selected_rows (GTK_LIST_BOX (widget->window_list)); ++ if (!selected_monitor_rows && !selected_window_rows) ++ return FALSE; ++ ++ for (l = selected_monitor_rows; l; l = l->next) ++ { ++ GtkWidget *monitor_widget = gtk_list_box_row_get_child (l->data); ++ Monitor *monitor; ++ ++ monitor = g_object_get_qdata (G_OBJECT (monitor_widget), ++ quark_monitor_widget_data); ++ ++ g_variant_builder_add (source_selections_builder, "(us)", ++ SCREEN_CAST_SOURCE_TYPE_MONITOR, ++ monitor_get_connector (monitor)); ++ } ++ g_list_free (selected_monitor_rows); ++ for (l = selected_window_rows; l; l = l->next) ++ { ++ GtkWidget *window_widget = gtk_list_box_row_get_child (l->data); ++ Window *window; ++ ++ window = g_object_get_qdata (G_OBJECT (window_widget), ++ quark_window_widget_data); ++ ++ g_variant_builder_add (source_selections_builder, "(ut)", ++ SCREEN_CAST_SOURCE_TYPE_WINDOW, ++ window_get_id (window)); ++ } ++ g_list_free (selected_window_rows); ++ ++ return TRUE; ++} ++ + static GtkWidget * + create_window_widget (Window *window) + { +@@ -131,7 +180,7 @@ create_monitor_widget (LogicalMonitor *logical_monitor) + } + + static gboolean +-should_skip_window (Window *window, ++should_skip_window (Window *window, + GtkWindow *toplevel) + { + g_autofree char *processed_app_id = NULL; +@@ -254,7 +303,7 @@ unselect_rows (GtkListBox *listbox) + } + + static void +-on_windows_changed (ShellIntrospect *shell_introspect, ++on_windows_changed (ShellIntrospect *shell_introspect, + ScreenCastWidget *widget) + { + update_windows_list (widget); +@@ -283,9 +332,9 @@ disconnect_windows_changed_listener (ScreenCastWidget *widget) + } + + static void +-on_stack_switch (GtkStack *stack, ++on_stack_switch (GtkStack *stack, + GParamSpec *pspec, +- gpointer *data) ++ gpointer *data) + { + ScreenCastWidget *widget = (ScreenCastWidget *)data; + GtkWidget *visible_child; +@@ -307,9 +356,9 @@ on_stack_switch (GtkStack *stack, + } + + static void +-on_row_activated (GtkListBox *box, ++on_row_activated (GtkListBox *box, + GtkListBoxRow *row, +- gpointer *data) ++ gpointer *data) + { + if (!row) + return; +@@ -378,7 +427,7 @@ schedule_selection_change (ScreenCastWidget *widget) + } + + static void +-on_selected_rows_changed (GtkListBox *box, ++on_selected_rows_changed (GtkListBox *box, + ScreenCastWidget *widget) + { + /* GtkListBox activates rows after selecting them, which prevents +@@ -403,59 +452,128 @@ update_list_box_header (GtkListBoxRow *row, + + static void + on_monitors_changed (DisplayStateTracker *display_state_tracker, +- ScreenCastWidget *widget) ++ ScreenCastWidget *widget) + { + update_monitors_list (widget); + } + +-static gboolean +-add_selections (ScreenCastWidget *widget, +- GVariantBuilder *source_selections_builder) ++/* ++ * GObject overrides ++ */ ++ ++static void ++screen_cast_widget_finalize (GObject *object) + { +- GList *selected_monitor_rows; +- GList *selected_window_rows; +- GList *l; ++ ScreenCastWidget *widget = SCREEN_CAST_WIDGET (object); + +- selected_monitor_rows = +- gtk_list_box_get_selected_rows (GTK_LIST_BOX (widget->monitor_list)); +- selected_window_rows = +- gtk_list_box_get_selected_rows (GTK_LIST_BOX (widget->window_list)); +- if (!selected_monitor_rows && !selected_window_rows) +- return FALSE; ++ g_signal_handler_disconnect (widget->display_state_tracker, ++ widget->monitors_changed_handler_id); + +- for (l = selected_monitor_rows; l; l = l->next) ++ if (widget->windows_changed_handler_id) ++ disconnect_windows_changed_listener (widget); ++ ++ if (widget->selection_changed_timeout_id > 0) + { +- GtkWidget *monitor_widget = gtk_list_box_row_get_child (l->data); +- Monitor *monitor; ++ g_source_remove (widget->selection_changed_timeout_id); ++ widget->selection_changed_timeout_id = 0; ++ } + +- monitor = g_object_get_qdata (G_OBJECT (monitor_widget), +- quark_monitor_widget_data); ++ G_OBJECT_CLASS (screen_cast_widget_parent_class)->finalize (object); ++} + +- g_variant_builder_add (source_selections_builder, "(us)", +- SCREEN_CAST_SOURCE_TYPE_MONITOR, +- monitor_get_connector (monitor)); +- } +- g_list_free (selected_monitor_rows); +- for (l = selected_window_rows; l; l = l->next) +- { +- GtkWidget *window_widget = gtk_list_box_row_get_child (l->data); +- Window *window; ++static void ++screen_cast_widget_class_init (ScreenCastWidgetClass *klass) ++{ ++ GObjectClass *object_class = G_OBJECT_CLASS (klass); ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + +- window = g_object_get_qdata (G_OBJECT (window_widget), +- quark_window_widget_data); ++ object_class->finalize = screen_cast_widget_finalize; + +- g_variant_builder_add (source_selections_builder, "(ut)", +- SCREEN_CAST_SOURCE_TYPE_WINDOW, +- window_get_id (window)); +- } +- g_list_free (selected_window_rows); ++ signals[HAS_SELECTION_CHANGED] = g_signal_new ("has-selection-changed", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, ++ NULL, ++ G_TYPE_NONE, 1, ++ G_TYPE_BOOLEAN); + +- return TRUE; ++ gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/portal/desktop/gnome/screencastwidget.ui"); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, source_type_switcher); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, source_type); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, monitor_selection); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, window_selection); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, monitor_heading); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, monitor_list); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, window_heading); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, window_list); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, window_list_scrolled); ++ ++ quark_monitor_widget_data = g_quark_from_static_string ("-monitor-widget-connector-quark"); ++ quark_window_widget_data = g_quark_from_static_string ("-window-widget-connector-quark"); ++} ++ ++static void ++screen_cast_widget_init (ScreenCastWidget *widget) ++{ ++ GtkScrolledWindow *scrolled_window; ++ GtkAdjustment *vadjustment; ++ ++ gtk_widget_init_template (GTK_WIDGET (widget)); ++ ++ screen_cast_widget_set_app_id (widget, NULL); ++ screen_cast_widget_set_allow_multiple (widget, FALSE); ++ ++ gtk_list_box_set_header_func (GTK_LIST_BOX (widget->monitor_list), ++ update_list_box_header, ++ NULL, NULL); ++ gtk_list_box_set_header_func (GTK_LIST_BOX (widget->window_list), ++ update_list_box_header, ++ NULL, NULL); ++ scrolled_window = GTK_SCROLLED_WINDOW (widget->window_list_scrolled); ++ vadjustment = gtk_scrolled_window_get_vadjustment (scrolled_window); ++ gtk_list_box_set_adjustment (GTK_LIST_BOX (widget->window_list), vadjustment); ++ ++ g_signal_connect (widget->source_type, "notify::visible-child", ++ G_CALLBACK (on_stack_switch), ++ widget); ++ g_signal_connect (widget->monitor_list, "row-activated", ++ G_CALLBACK (on_row_activated), ++ NULL); ++ g_signal_connect (widget->window_list, "row-activated", ++ G_CALLBACK (on_row_activated), ++ NULL); ++ g_signal_connect (widget->monitor_list, "selected-rows-changed", ++ G_CALLBACK (on_selected_rows_changed), ++ widget); ++ g_signal_connect (widget->window_list, "selected-rows-changed", ++ G_CALLBACK (on_selected_rows_changed), ++ widget); ++ ++ widget->display_state_tracker = display_state_tracker_get (); ++ widget->monitors_changed_handler_id = ++ g_signal_connect (widget->display_state_tracker, ++ "monitors-changed", ++ G_CALLBACK (on_monitors_changed), ++ widget); ++ widget->shell_introspect = shell_introspect_get (); ++ ++ update_monitors_list (widget); ++ update_windows_list (widget); ++ ++ gtk_widget_show (widget->monitor_list); ++ gtk_widget_show (widget->window_list); ++} ++ ++void ++init_screen_cast_widget (void) ++{ ++ g_type_ensure (screen_cast_widget_get_type ()); + } + + void + screen_cast_widget_add_selections (ScreenCastWidget *widget, +- GVariantBuilder *selections_builder) ++ GVariantBuilder *selections_builder) + { + GVariantBuilder source_selections_builder; + +@@ -474,7 +592,7 @@ screen_cast_widget_add_selections (ScreenCastWidget *widget, + + void + screen_cast_widget_set_app_id (ScreenCastWidget *widget, +- const char *app_id) ++ const char *app_id) + { + g_autofree char *monitor_heading = NULL; + g_autofree char *window_heading = NULL; +@@ -508,7 +626,7 @@ screen_cast_widget_set_app_id (ScreenCastWidget *widget, + + void + screen_cast_widget_set_allow_multiple (ScreenCastWidget *widget, +- gboolean multiple) ++ gboolean multiple) + { + gtk_list_box_set_selection_mode (GTK_LIST_BOX (widget->monitor_list), + multiple ? GTK_SELECTION_MULTIPLE +@@ -519,8 +637,8 @@ screen_cast_widget_set_allow_multiple (ScreenCastWidget *widget, + } + + void +-screen_cast_widget_set_source_types (ScreenCastWidget *screen_cast_widget, +- ScreenCastSourceType source_types) ++screen_cast_widget_set_source_types (ScreenCastWidget *screen_cast_widget, ++ ScreenCastSourceType source_types) + { + if (source_types & SCREEN_CAST_SOURCE_TYPE_MONITOR) + gtk_widget_show (screen_cast_widget->monitor_selection); +@@ -531,113 +649,3 @@ screen_cast_widget_set_source_types (ScreenCastWidget *screen_cast_widget, + if (__builtin_popcount (source_types) > 1) + gtk_widget_show (screen_cast_widget->source_type_switcher); + } +- +-static void +-screen_cast_widget_finalize (GObject *object) +-{ +- ScreenCastWidget *widget = SCREEN_CAST_WIDGET (object); +- +- g_signal_handler_disconnect (widget->display_state_tracker, +- widget->monitors_changed_handler_id); +- +- if (widget->windows_changed_handler_id) +- disconnect_windows_changed_listener (widget); +- +- if (widget->selection_changed_timeout_id > 0) +- { +- g_source_remove (widget->selection_changed_timeout_id); +- widget->selection_changed_timeout_id = 0; +- } +- +- G_OBJECT_CLASS (screen_cast_widget_parent_class)->finalize (object); +-} +- +-static void +-screen_cast_widget_init (ScreenCastWidget *widget) +-{ +- GtkScrolledWindow *scrolled_window; +- GtkAdjustment *vadjustment; +- +- gtk_widget_init_template (GTK_WIDGET (widget)); +- +- screen_cast_widget_set_app_id (widget, NULL); +- screen_cast_widget_set_allow_multiple (widget, FALSE); +- +- gtk_list_box_set_header_func (GTK_LIST_BOX (widget->monitor_list), +- update_list_box_header, +- NULL, NULL); +- gtk_list_box_set_header_func (GTK_LIST_BOX (widget->window_list), +- update_list_box_header, +- NULL, NULL); +- scrolled_window = GTK_SCROLLED_WINDOW (widget->window_list_scrolled); +- vadjustment = gtk_scrolled_window_get_vadjustment (scrolled_window); +- gtk_list_box_set_adjustment (GTK_LIST_BOX (widget->window_list), vadjustment); +- +- g_signal_connect (widget->source_type, "notify::visible-child", +- G_CALLBACK (on_stack_switch), +- widget); +- g_signal_connect (widget->monitor_list, "row-activated", +- G_CALLBACK (on_row_activated), +- NULL); +- g_signal_connect (widget->window_list, "row-activated", +- G_CALLBACK (on_row_activated), +- NULL); +- g_signal_connect (widget->monitor_list, "selected-rows-changed", +- G_CALLBACK (on_selected_rows_changed), +- widget); +- g_signal_connect (widget->window_list, "selected-rows-changed", +- G_CALLBACK (on_selected_rows_changed), +- widget); +- +- widget->display_state_tracker = display_state_tracker_get (); +- widget->monitors_changed_handler_id = +- g_signal_connect (widget->display_state_tracker, +- "monitors-changed", +- G_CALLBACK (on_monitors_changed), +- widget); +- widget->shell_introspect = shell_introspect_get (); +- +- update_monitors_list (widget); +- update_windows_list (widget); +- +- gtk_widget_show (widget->monitor_list); +- gtk_widget_show (widget->window_list); +-} +- +-static void +-screen_cast_widget_class_init (ScreenCastWidgetClass *klass) +-{ +- GObjectClass *object_class = G_OBJECT_CLASS (klass); +- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); +- +- object_class->finalize = screen_cast_widget_finalize; +- +- signals[HAS_SELECTION_CHANGED] = g_signal_new ("has-selection-changed", +- G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, +- 0, +- NULL, NULL, +- NULL, +- G_TYPE_NONE, 1, +- G_TYPE_BOOLEAN); +- +- gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/portal/desktop/gnome/screencastwidget.ui"); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, source_type_switcher); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, source_type); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, monitor_selection); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, window_selection); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, monitor_heading); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, monitor_list); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, window_heading); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, window_list); +- gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, window_list_scrolled); +- +- quark_monitor_widget_data = g_quark_from_static_string ("-monitor-widget-connector-quark"); +- quark_window_widget_data = g_quark_from_static_string ("-window-widget-connector-quark"); +-} +- +-void +-init_screen_cast_widget (void) +-{ +- g_type_ensure (screen_cast_widget_get_type ()); +-} +diff --git a/src/screencastwidget.h b/src/screencastwidget.h +index 3a1ebd2..34360a3 100644 +--- a/src/screencastwidget.h ++++ b/src/screencastwidget.h +@@ -31,13 +31,13 @@ G_DECLARE_FINAL_TYPE (ScreenCastWidget, screen_cast_widget, + void init_screen_cast_widget (void); + + void screen_cast_widget_set_app_id (ScreenCastWidget *widget, +- const char *app_id); ++ const char *app_id); + + void screen_cast_widget_set_allow_multiple (ScreenCastWidget *widget, +- gboolean multiple); ++ gboolean multiple); + +-void screen_cast_widget_set_source_types (ScreenCastWidget *screen_cast_widget, +- ScreenCastSourceType source_types); ++void screen_cast_widget_set_source_types (ScreenCastWidget *screen_cast_widget, ++ ScreenCastSourceType source_types); + + void screen_cast_widget_add_selections (ScreenCastWidget *widget, +- GVariantBuilder *selections_builder); ++ GVariantBuilder *selections_builder); +diff --git a/src/screencastwidget.ui b/src/screencastwidget.ui +index 2417ca5..fb83b94 100644 +--- a/src/screencastwidget.ui ++++ b/src/screencastwidget.ui +@@ -19,6 +19,7 @@ + + + crossfade ++ + + + windows_page +@@ -80,6 +81,8 @@ + + + ++ ++ + + + monitors_page diff --git a/00014-screencasts-duplicate-monitor-and-window-stream-info.patch b/00014-screencasts-duplicate-monitor-and-window-stream-info.patch new file mode 100644 index 0000000..49acf33 --- /dev/null +++ b/00014-screencasts-duplicate-monitor-and-window-stream-info.patch @@ -0,0 +1,223 @@ +diff --git a/src/displaystatetracker.c b/src/displaystatetracker.c +index fc9109e..ebe1105 100644 +--- a/src/displaystatetracker.c ++++ b/src/displaystatetracker.c +@@ -62,7 +62,7 @@ G_DEFINE_TYPE (DisplayStateTracker, display_state_tracker, G_TYPE_OBJECT) + + static DisplayStateTracker *_display_state_tracker; + +-static void ++void + monitor_free (Monitor *monitor) + { + g_free (monitor->connector); +@@ -71,6 +71,19 @@ monitor_free (Monitor *monitor) + g_free (monitor); + } + ++Monitor * ++monitor_dup (Monitor *monitor) ++{ ++ Monitor *new_monitor; ++ ++ new_monitor = g_new0 (Monitor, 1); ++ new_monitor->connector = g_strdup (monitor->connector); ++ new_monitor->match_string = g_strdup (monitor->match_string); ++ new_monitor->display_name = g_strdup (monitor->display_name); ++ ++ return new_monitor; ++} ++ + const char * + monitor_get_connector (Monitor *monitor) + { +diff --git a/src/displaystatetracker.h b/src/displaystatetracker.h +index 1c3a7dc..e438dda 100644 +--- a/src/displaystatetracker.h ++++ b/src/displaystatetracker.h +@@ -26,6 +26,10 @@ typedef struct _LogicalMonitor LogicalMonitor; + G_DECLARE_FINAL_TYPE (DisplayStateTracker, display_state_tracker, + DISPLAY, STATE_TRACKER, GObject) + ++void monitor_free (Monitor *monitor); ++ ++Monitor * monitor_dup (Monitor *monitor); ++ + const char * monitor_get_connector (Monitor *monitor); + + const char * monitor_get_match_string (Monitor *monitor); +diff --git a/src/screencast.c b/src/screencast.c +index 0a15033..724ffc9 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -469,6 +469,22 @@ find_best_window_by_app_id_and_title (const char *app_id, + return best_match; + } + ++void ++screen_cast_stream_info_free (ScreenCastStreamInfo *info) ++{ ++ switch (info->type) ++ { ++ case SCREEN_CAST_SOURCE_TYPE_MONITOR: ++ g_clear_pointer (&info->data.monitor, monitor_free); ++ break; ++ case SCREEN_CAST_SOURCE_TYPE_WINDOW: ++ g_clear_pointer (&info->data.window, window_free); ++ break; ++ } ++ ++ g_free (info); ++} ++ + static gboolean + restore_stream_from_data (ScreenCastSession *screen_cast_session) + +@@ -486,7 +502,8 @@ restore_stream_from_data (ScreenCastSession *screen_cast_session) + if (!screen_cast_session->restored.data) + return FALSE; + +- streams = g_ptr_array_new_with_free_func (g_free); ++ streams = ++ g_ptr_array_new_with_free_func ((GDestroyNotify) screen_cast_stream_info_free); + + g_variant_get (screen_cast_session->restored.data, + RESTORE_VARIANT_TYPE, +@@ -512,7 +529,7 @@ restore_stream_from_data (ScreenCastSession *screen_cast_session) + + info = g_new0 (ScreenCastStreamInfo, 1); + info->type = SCREEN_CAST_SOURCE_TYPE_MONITOR; +- info->data.monitor = monitor; ++ info->data.monitor = monitor_dup (monitor); + info->id = id; + g_ptr_array_add (streams, info); + } +@@ -537,7 +554,7 @@ restore_stream_from_data (ScreenCastSession *screen_cast_session) + + info = g_new0 (ScreenCastStreamInfo, 1); + info->type = SCREEN_CAST_SOURCE_TYPE_WINDOW; +- info->data.window = window; ++ info->data.window = window_dup (window); + info->id = id; + g_ptr_array_add (streams, info); + } +diff --git a/src/screencast.h b/src/screencast.h +index 3d64b4a..ae8988d 100644 +--- a/src/screencast.h ++++ b/src/screencast.h +@@ -64,3 +64,5 @@ typedef struct + + gboolean screen_cast_init (GDBusConnection *connection, + GError **error); ++ ++void screen_cast_stream_info_free (ScreenCastStreamInfo *info); +diff --git a/src/screencastwidget.c b/src/screencastwidget.c +index 11743f7..a40c824 100644 +--- a/src/screencastwidget.c ++++ b/src/screencastwidget.c +@@ -101,9 +101,10 @@ create_window_widget (Window *window) + gtk_widget_set_margin_bottom (window_label, 12); + gtk_box_append (GTK_BOX (window_widget), window_label); + +- g_object_set_qdata (G_OBJECT (window_widget), +- quark_window_widget_data, +- window); ++ g_object_set_qdata_full (G_OBJECT (window_widget), ++ quark_window_widget_data, ++ window_dup (window), ++ (GDestroyNotify) window_free); + return window_widget; + } + +@@ -123,9 +124,12 @@ create_monitor_widget (LogicalMonitor *logical_monitor) + GtkWidget *monitor_label; + + if (!l->prev) +- g_object_set_qdata (G_OBJECT (monitor_widget), +- quark_monitor_widget_data, +- monitor); ++ { ++ g_object_set_qdata_full (G_OBJECT (monitor_widget), ++ quark_monitor_widget_data, ++ monitor_dup (monitor), ++ (GDestroyNotify) monitor_free); ++ } + + monitor_label = gtk_label_new (monitor_get_display_name (monitor)); + gtk_widget_set_margin_top (monitor_label, 12); +@@ -599,7 +603,8 @@ screen_cast_widget_get_selected_streams (ScreenCastWidget *self) + uint32_t id = 0; + GList *l; + +- streams = g_ptr_array_new_with_free_func (g_free); ++ streams = ++ g_ptr_array_new_with_free_func ((GDestroyNotify) screen_cast_stream_info_free); + + selected_monitor_rows = + gtk_list_box_get_selected_rows (GTK_LIST_BOX (self->monitor_list)); +@@ -619,7 +624,7 @@ screen_cast_widget_get_selected_streams (ScreenCastWidget *self) + + info = g_new0 (ScreenCastStreamInfo, 1); + info->type = SCREEN_CAST_SOURCE_TYPE_MONITOR; +- info->data.monitor = monitor; ++ info->data.monitor = monitor_dup (monitor); + info->id = id++; + g_ptr_array_add (streams, info); + } +@@ -634,7 +639,7 @@ screen_cast_widget_get_selected_streams (ScreenCastWidget *self) + + info = g_new0 (ScreenCastStreamInfo, 1); + info->type = SCREEN_CAST_SOURCE_TYPE_WINDOW; +- info->data.window = window; ++ info->data.window = window_dup (window); + info->id = id++; + g_ptr_array_add (streams, info); + } +diff --git a/src/shellintrospect.c b/src/shellintrospect.c +index 8a81086..d1bcc8c 100644 +--- a/src/shellintrospect.c ++++ b/src/shellintrospect.c +@@ -62,7 +62,7 @@ static guint signals[N_SIGNALS]; + + static ShellIntrospect *_shell_introspect; + +-static void ++void + window_free (Window *window) + { + g_free (window->title); +@@ -70,6 +70,19 @@ window_free (Window *window) + g_free (window); + } + ++Window * ++window_dup (Window *window) ++{ ++ Window *new_window; ++ ++ new_window = g_new0 (Window, 1); ++ new_window->id = window->id; ++ new_window->title = g_strdup (window->title); ++ new_window->app_id = g_strdup (window->app_id); ++ ++ return new_window; ++} ++ + static void + get_windows_cb (GObject *source_object, + GAsyncResult *res, +diff --git a/src/shellintrospect.h b/src/shellintrospect.h +index f63ecee..ea4188f 100644 +--- a/src/shellintrospect.h ++++ b/src/shellintrospect.h +@@ -34,6 +34,10 @@ void shell_introspect_ref_listeners (ShellIntrospect *shell_introspect); + + void shell_introspect_unref_listeners (ShellIntrospect *shell_introspect); + ++void window_free (Window *window); ++ ++Window * window_dup (Window *window); ++ + const char * window_get_app_id (Window *window); + + const char * window_get_title (Window *window); diff --git a/0002-screencast-cleanup-unnecessary-forward-declaration.patch b/0002-screencast-cleanup-unnecessary-forward-declaration.patch new file mode 100644 index 0000000..a879398 --- /dev/null +++ b/0002-screencast-cleanup-unnecessary-forward-declaration.patch @@ -0,0 +1,23 @@ +From a7c03eb9642a244f5623bf5bbe0e29f759554567 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Wed, 10 Nov 2021 15:52:37 -0300 +Subject: [PATCH] screencast: Cleanup unnecessary forward declaration + +--- + src/screencast.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/src/screencast.c b/src/screencast.c +index 38d1d05..98390ab 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -77,9 +77,6 @@ static GnomeScreenCast *gnome_screen_cast; + GType screen_cast_session_get_type (void); + G_DEFINE_TYPE (ScreenCastSession, screen_cast_session, session_get_type ()) + +-static void +-start_done (ScreenCastSession *session); +- + static gboolean + start_session (ScreenCastSession *session, + GVariant *selections, diff --git a/0003-screencast-more-code-shuffling-and-cleanups.patch b/0003-screencast-more-code-shuffling-and-cleanups.patch new file mode 100644 index 0000000..25a3f2c --- /dev/null +++ b/0003-screencast-more-code-shuffling-and-cleanups.patch @@ -0,0 +1,452 @@ +From 367f05b980ab780ae3b3380164f2135ce4f487fa Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Wed, 10 Nov 2021 16:04:59 -0300 +Subject: [PATCH] screencast: More code shuffling and cleanups + +No funcional changes. +--- + src/screencast.c | 350 +++++++++++++++++++++++------------------------ + 1 file changed, 168 insertions(+), 182 deletions(-) + +diff --git a/src/screencast.c b/src/screencast.c +index 98390ab..4ba67aa 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -82,10 +82,6 @@ start_session (ScreenCastSession *session, + GVariant *selections, + GError **error); + +-static void +-cancel_start_session (ScreenCastSession *session, +- int response); +- + static gboolean + is_screen_cast_session (Session *session) + { +@@ -108,10 +104,23 @@ screen_cast_dialog_handle_close (ScreenCastDialogHandle *dialog_handle) + screen_cast_dialog_handle_free (dialog_handle); + } + ++static void ++cancel_start_session (ScreenCastSession *screen_cast_session, ++ int response) ++{ ++ GVariantBuilder results_builder; ++ ++ g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); ++ xdp_impl_screen_cast_complete_start (XDP_IMPL_SCREEN_CAST (impl), ++ screen_cast_session->start_invocation, ++ response, ++ g_variant_builder_end (&results_builder)); ++} ++ + static gboolean +-handle_close (XdpImplRequest *object, +- GDBusMethodInvocation *invocation, +- ScreenCastDialogHandle *dialog_handle) ++on_request_handle_close_cb (XdpImplRequest *object, ++ GDBusMethodInvocation *invocation, ++ ScreenCastDialogHandle *dialog_handle) + { + cancel_start_session (dialog_handle->session, 2); + +@@ -121,10 +130,10 @@ handle_close (XdpImplRequest *object, + } + + static void +-screen_cast_dialog_done (GtkWidget *widget, +- int dialog_response, +- GVariant *selections, +- ScreenCastDialogHandle *dialog_handle) ++on_screen_cast_dialog_done_cb (GtkWidget *widget, ++ int dialog_response, ++ GVariant *selections, ++ ScreenCastDialogHandle *dialog_handle) + { + int response; + +@@ -165,10 +174,10 @@ screen_cast_dialog_done (GtkWidget *widget, + } + + static ScreenCastDialogHandle * +-create_screen_cast_dialog (ScreenCastSession *session, ++create_screen_cast_dialog (ScreenCastSession *session, + GDBusMethodInvocation *invocation, +- Request *request, +- const char *parent_window) ++ Request *request, ++ const char *parent_window) + { + ScreenCastDialogHandle *dialog_handle; + ExternalWindow *external_parent; +@@ -210,10 +219,9 @@ create_screen_cast_dialog (ScreenCastSession *session, + dialog_handle->dialog = dialog; + + g_signal_connect (request, "handle-close", +- G_CALLBACK (handle_close), dialog_handle); +- ++ G_CALLBACK (on_request_handle_close_cb), dialog_handle); + g_signal_connect (dialog, "done", +- G_CALLBACK (screen_cast_dialog_done), dialog_handle); ++ G_CALLBACK (on_screen_cast_dialog_done_cb), dialog_handle); + + gtk_widget_realize (dialog); + +@@ -226,145 +234,6 @@ create_screen_cast_dialog (ScreenCastSession *session, + return dialog_handle; + } + +-static ScreenCastSession * +-screen_cast_session_new (const char *app_id, +- const char *session_handle) +-{ +- ScreenCastSession *screen_cast_session; +- +- screen_cast_session = g_object_new (screen_cast_session_get_type (), +- "id", session_handle, +- NULL); +- +- return screen_cast_session; +-} +- +-static gboolean +-handle_create_session (XdpImplScreenCast *object, +- GDBusMethodInvocation *invocation, +- const char *arg_handle, +- const char *arg_session_handle, +- const char *arg_app_id, +- GVariant *arg_options) +-{ +- g_autoptr(GError) error = NULL; +- int response; +- Session *session; +- GVariantBuilder results_builder; +- +- session = (Session *)screen_cast_session_new (arg_app_id, +- arg_session_handle); +- +- if (!session_export (session, +- g_dbus_method_invocation_get_connection (invocation), +- &error)) +- { +- g_clear_object (&session); +- g_warning ("Failed to create screen cast session: %s", error->message); +- response = 2; +- goto out; +- } +- +- response = 0; +- +-out: +- g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); +- xdp_impl_screen_cast_complete_create_session (object, +- invocation, +- response, +- g_variant_builder_end (&results_builder)); +- +- return TRUE; +-} +- +-static gboolean +-handle_select_sources (XdpImplScreenCast *object, +- GDBusMethodInvocation *invocation, +- const char *arg_handle, +- const char *arg_session_handle, +- const char *arg_app_id, +- GVariant *arg_options) +-{ +- Session *session; +- int response; +- uint32_t types; +- gboolean multiple; +- ScreenCastCursorMode cursor_mode; +- ScreenCastSelection select; +- GVariantBuilder results_builder; +- GVariant *results; +- +- session = lookup_session (arg_session_handle); +- if (!session) +- { +- g_warning ("Tried to select sources on non-existing %s", arg_session_handle); +- response = 2; +- goto out; +- } +- +- if (!g_variant_lookup (arg_options, "multiple", "b", &multiple)) +- multiple = FALSE; +- +- if (!g_variant_lookup (arg_options, "types", "u", &types)) +- types = SCREEN_CAST_SOURCE_TYPE_MONITOR; +- +- if (!(types & (SCREEN_CAST_SOURCE_TYPE_MONITOR | +- SCREEN_CAST_SOURCE_TYPE_WINDOW))) +- { +- g_warning ("Unknown screen cast source type"); +- response = 2; +- goto out; +- } +- +- if (!g_variant_lookup (arg_options, "cursor_mode", "u", &cursor_mode)) +- cursor_mode = SCREEN_CAST_CURSOR_MODE_HIDDEN; +- +- switch (cursor_mode) +- { +- case SCREEN_CAST_CURSOR_MODE_HIDDEN: +- case SCREEN_CAST_CURSOR_MODE_EMBEDDED: +- case SCREEN_CAST_CURSOR_MODE_METADATA: +- break; +- default: +- g_warning ("Unknown screen cast cursor mode"); +- response = 2; +- goto out; +- } +- +- select.multiple = multiple; +- select.source_types = types; +- select.cursor_mode = cursor_mode; +- +- if (is_screen_cast_session (session)) +- { +- ScreenCastSession *screen_cast_session = (ScreenCastSession *)session; +- +- screen_cast_session->select = select; +- response = 0; +- } +- else if (is_remote_desktop_session (session)) +- { +- RemoteDesktopSession *remote_desktop_session = +- (RemoteDesktopSession *)session; +- +- remote_desktop_session_sources_selected (remote_desktop_session, &select); +- response = 0; +- } +- else +- { +- g_warning ("Tried to select sources on invalid session type"); +- response = 2; +- } +- +-out: +- g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); +- results = g_variant_builder_end (&results_builder); +- xdp_impl_screen_cast_complete_select_sources (object, invocation, +- response, results); +- +- return TRUE; +-} +- + static void + start_done (ScreenCastSession *screen_cast_session) + { +@@ -391,14 +260,14 @@ start_done (ScreenCastSession *screen_cast_session) + + static void + on_gnome_screen_cast_session_ready (GnomeScreenCastSession *gnome_screen_cast_session, +- ScreenCastSession *screen_cast_session) ++ ScreenCastSession *screen_cast_session) + { + start_done (screen_cast_session); + } + + static void + on_gnome_screen_cast_session_closed (GnomeScreenCastSession *gnome_screen_cast_session, +- ScreenCastSession *screen_cast_session) ++ ScreenCastSession *screen_cast_session) + { + session_close ((Session *)screen_cast_session); + } +@@ -441,27 +310,14 @@ start_session (ScreenCastSession *screen_cast_session, + return TRUE; + } + +-static void +-cancel_start_session (ScreenCastSession *screen_cast_session, +- int response) +-{ +- GVariantBuilder results_builder; +- +- g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); +- xdp_impl_screen_cast_complete_start (XDP_IMPL_SCREEN_CAST (impl), +- screen_cast_session->start_invocation, +- response, +- g_variant_builder_end (&results_builder)); +-} +- + static gboolean +-handle_start (XdpImplScreenCast *object, ++handle_start (XdpImplScreenCast *object, + GDBusMethodInvocation *invocation, +- const char *arg_handle, +- const char *arg_session_handle, +- const char *arg_app_id, +- const char *arg_parent_window, +- GVariant *arg_options) ++ const char *arg_handle, ++ const char *arg_session_handle, ++ const char *arg_app_id, ++ const char *arg_parent_window, ++ GVariant *arg_options) + { + const char *sender; + g_autoptr(Request) request = NULL; +@@ -507,6 +363,133 @@ err: + return TRUE; + } + ++static gboolean ++handle_select_sources (XdpImplScreenCast *object, ++ GDBusMethodInvocation *invocation, ++ const char *arg_handle, ++ const char *arg_session_handle, ++ const char *arg_app_id, ++ GVariant *arg_options) ++{ ++ Session *session; ++ int response; ++ uint32_t types; ++ gboolean multiple; ++ ScreenCastCursorMode cursor_mode; ++ ScreenCastSelection select; ++ GVariantBuilder results_builder; ++ GVariant *results; ++ ++ session = lookup_session (arg_session_handle); ++ if (!session) ++ { ++ g_warning ("Tried to select sources on non-existing %s", arg_session_handle); ++ response = 2; ++ goto out; ++ } ++ ++ if (!g_variant_lookup (arg_options, "multiple", "b", &multiple)) ++ multiple = FALSE; ++ ++ if (!g_variant_lookup (arg_options, "types", "u", &types)) ++ types = SCREEN_CAST_SOURCE_TYPE_MONITOR; ++ ++ if (!(types & (SCREEN_CAST_SOURCE_TYPE_MONITOR | ++ SCREEN_CAST_SOURCE_TYPE_WINDOW))) ++ { ++ g_warning ("Unknown screen cast source type"); ++ response = 2; ++ goto out; ++ } ++ ++ if (!g_variant_lookup (arg_options, "cursor_mode", "u", &cursor_mode)) ++ cursor_mode = SCREEN_CAST_CURSOR_MODE_HIDDEN; ++ ++ switch (cursor_mode) ++ { ++ case SCREEN_CAST_CURSOR_MODE_HIDDEN: ++ case SCREEN_CAST_CURSOR_MODE_EMBEDDED: ++ case SCREEN_CAST_CURSOR_MODE_METADATA: ++ break; ++ default: ++ g_warning ("Unknown screen cast cursor mode"); ++ response = 2; ++ goto out; ++ } ++ ++ select.multiple = multiple; ++ select.source_types = types; ++ select.cursor_mode = cursor_mode; ++ ++ if (is_screen_cast_session (session)) ++ { ++ ScreenCastSession *screen_cast_session = (ScreenCastSession *)session; ++ ++ screen_cast_session->select = select; ++ response = 0; ++ } ++ else if (is_remote_desktop_session (session)) ++ { ++ RemoteDesktopSession *remote_desktop_session = ++ (RemoteDesktopSession *)session; ++ ++ remote_desktop_session_sources_selected (remote_desktop_session, &select); ++ response = 0; ++ } ++ else ++ { ++ g_warning ("Tried to select sources on invalid session type"); ++ response = 2; ++ } ++ ++out: ++ g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); ++ results = g_variant_builder_end (&results_builder); ++ xdp_impl_screen_cast_complete_select_sources (object, invocation, ++ response, results); ++ ++ return TRUE; ++} ++ ++static gboolean ++handle_create_session (XdpImplScreenCast *object, ++ GDBusMethodInvocation *invocation, ++ const char *arg_handle, ++ const char *arg_session_handle, ++ const char *arg_app_id, ++ GVariant *arg_options) ++{ ++ g_autoptr(GError) error = NULL; ++ int response; ++ Session *session; ++ GVariantBuilder results_builder; ++ ++ session = g_object_new (screen_cast_session_get_type (), ++ "id", arg_session_handle, ++ NULL); ++ ++ if (!session_export (session, ++ g_dbus_method_invocation_get_connection (invocation), ++ &error)) ++ { ++ g_clear_object (&session); ++ g_warning ("Failed to create screen cast session: %s", error->message); ++ response = 2; ++ goto out; ++ } ++ ++ response = 0; ++ ++out: ++ g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); ++ xdp_impl_screen_cast_complete_create_session (object, ++ invocation, ++ response, ++ g_variant_builder_end (&results_builder)); ++ ++ return TRUE; ++} ++ + static void + on_gnome_screen_cast_enabled (GnomeScreenCast *gnome_screen_cast) + { +@@ -535,9 +518,12 @@ on_gnome_screen_cast_enabled (GnomeScreenCast *gnome_screen_cast) + + available_cursor_modes = SCREEN_CAST_CURSOR_MODE_NONE; + if (gnome_api_version >= 2) +- available_cursor_modes |= (SCREEN_CAST_CURSOR_MODE_HIDDEN | +- SCREEN_CAST_CURSOR_MODE_EMBEDDED | +- SCREEN_CAST_CURSOR_MODE_METADATA); ++ { ++ available_cursor_modes |= SCREEN_CAST_CURSOR_MODE_HIDDEN | ++ SCREEN_CAST_CURSOR_MODE_EMBEDDED | ++ SCREEN_CAST_CURSOR_MODE_METADATA; ++ } ++ + g_object_set (G_OBJECT (impl), + "available-cursor-modes", available_cursor_modes, + NULL); +@@ -620,8 +606,8 @@ screen_cast_session_class_init (ScreenCastSessionClass *klass) + } + + gboolean +-screen_cast_init (GDBusConnection *connection, +- GError **error) ++screen_cast_init (GDBusConnection *connection, ++ GError **error) + { + impl_connection = connection; + gnome_screen_cast = gnome_screen_cast_new (connection); diff --git a/0004-screencastwidget-rework-selection-api.patch b/0004-screencastwidget-rework-selection-api.patch new file mode 100644 index 0000000..690fca4 --- /dev/null +++ b/0004-screencastwidget-rework-selection-api.patch @@ -0,0 +1,744 @@ +From 0f6957254e32ab6dc1c132c0f7623bbac31309f6 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Wed, 10 Nov 2021 19:54:39 -0300 +Subject: [PATCH] screencastwidget: Rework selection APIs + +Instead of passing data around using GVariants, add +properly types variables and structures all around. +This will vastly simplify future commits introducing +screencast stream restore. +--- + src/gnomescreencast.c | 51 ++++++------- + src/gnomescreencast.h | 8 +- + src/remotedesktop.c | 40 +++++----- + src/remotedesktopdialog.c | 35 ++++----- + src/screencast.c | 151 +++++++++++++++++--------------------- + src/screencast.h | 12 +++ + src/screencastdialog.c | 15 ++-- + src/screencastwidget.c | 132 +++++++++++++++------------------ + src/screencastwidget.h | 3 +- + 9 files changed, 203 insertions(+), 244 deletions(-) + +diff --git a/src/gnomescreencast.c b/src/gnomescreencast.c +index abf5b53..3f8539c 100644 +--- a/src/gnomescreencast.c ++++ b/src/gnomescreencast.c +@@ -274,10 +274,10 @@ cursor_mode_to_gnome_cursor_mode (ScreenCastCursorMode cursor_mode) + } + + static gboolean +-gnome_screen_cast_session_record_window (GnomeScreenCastSession *gnome_screen_cast_session, +- const guint64 id, +- ScreenCastSelection *select, +- GError **error) ++gnome_screen_cast_session_record_window (GnomeScreenCastSession *gnome_screen_cast_session, ++ Window *window, ++ ScreenCastSelection *select, ++ GError **error) + { + OrgGnomeMutterScreenCastSession *session_proxy = + gnome_screen_cast_session->proxy; +@@ -292,7 +292,7 @@ gnome_screen_cast_session_record_window (GnomeScreenCastSession *gnome_screen_ca + g_variant_builder_init (&properties_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&properties_builder, "{sv}", + "window-id", +- g_variant_new_uint64 (id)); ++ g_variant_new_uint64 (window_get_id (window))); + if (select->cursor_mode) + { + uint32_t gnome_cursor_mode; +@@ -356,10 +356,10 @@ gnome_screen_cast_session_record_window (GnomeScreenCastSession *gnome_screen_ca + } + + static gboolean +-gnome_screen_cast_session_record_monitor (GnomeScreenCastSession *gnome_screen_cast_session, +- const char *connector, +- ScreenCastSelection *select, +- GError **error) ++gnome_screen_cast_session_record_monitor (GnomeScreenCastSession *gnome_screen_cast_session, ++ Monitor *monitor, ++ ScreenCastSelection *select, ++ GError **error) + { + OrgGnomeMutterScreenCastSession *session_proxy = + gnome_screen_cast_session->proxy; +@@ -370,6 +370,7 @@ gnome_screen_cast_session_record_monitor (GnomeScreenCastSession *gnome_screen_c + OrgGnomeMutterScreenCastStream *stream_proxy; + GnomeScreenCastStream *stream; + GVariant *parameters; ++ const char *connector; + + g_variant_builder_init (&properties_builder, G_VARIANT_TYPE_VARDICT); + if (select->cursor_mode) +@@ -383,6 +384,7 @@ gnome_screen_cast_session_record_monitor (GnomeScreenCastSession *gnome_screen_c + } + properties = g_variant_builder_end (&properties_builder); + ++ connector = monitor_get_connector (monitor); + if (!org_gnome_mutter_screen_cast_session_call_record_monitor_sync (session_proxy, + connector, + properties, +@@ -436,40 +438,29 @@ gnome_screen_cast_session_record_monitor (GnomeScreenCastSession *gnome_screen_c + } + + gboolean +-gnome_screen_cast_session_record_selections (GnomeScreenCastSession *gnome_screen_cast_session, +- GVariant *selections, +- ScreenCastSelection *select, +- GError **error) ++gnome_screen_cast_session_record_selections (GnomeScreenCastSession *gnome_screen_cast_session, ++ GPtrArray *streams, ++ ScreenCastSelection *select, ++ GError **error) + { +- GVariantIter selections_iter; +- GVariant *selection; ++ guint i; + +- g_variant_iter_init (&selections_iter, selections); +- while ((selection = g_variant_iter_next_value (&selections_iter))) ++ for (i = 0; i < streams->len; i++) + { +- ScreenCastSourceType source_type; +- g_autofree char *key = NULL; +- g_autoptr(GVariant) variant = NULL; +- guint64 id; ++ ScreenCastStreamInfo *info = g_ptr_array_index (streams, i); + +- g_variant_get (selection, "(u?)", +- &source_type, +- &variant); +- +- switch (source_type) ++ switch (info->type) + { + case SCREEN_CAST_SOURCE_TYPE_MONITOR: +- key = g_variant_dup_string (variant, NULL); + if (!gnome_screen_cast_session_record_monitor (gnome_screen_cast_session, +- key, ++ info->data.monitor, + select, + error)) + return FALSE; + break; + case SCREEN_CAST_SOURCE_TYPE_WINDOW: +- id = g_variant_get_uint64 (variant); + if (!gnome_screen_cast_session_record_window (gnome_screen_cast_session, +- id, ++ info->data.window, + select, + error)) + return FALSE; +diff --git a/src/gnomescreencast.h b/src/gnomescreencast.h +index 06e4e1e..748cf7d 100644 +--- a/src/gnomescreencast.h ++++ b/src/gnomescreencast.h +@@ -34,10 +34,10 @@ const char * gnome_screen_cast_session_get_stream_path_from_id (GnomeScreenCastS + void gnome_screen_cast_session_add_stream_properties (GnomeScreenCastSession *gnome_screen_cast_session, + GVariantBuilder *streams_builder); + +-gboolean gnome_screen_cast_session_record_selections (GnomeScreenCastSession *gnome_screen_cast_session, +- GVariant *selections, +- ScreenCastSelection *select, +- GError **error); ++gboolean gnome_screen_cast_session_record_selections (GnomeScreenCastSession *gnome_screen_cast_session, ++ GPtrArray *streams, ++ ScreenCastSelection *select, ++ GError **error); + + gboolean gnome_screen_cast_session_stop (GnomeScreenCastSession *gnome_screen_cast_session, + GError **error); +diff --git a/src/remotedesktop.c b/src/remotedesktop.c +index a1c688a..46d1dcc 100644 +--- a/src/remotedesktop.c ++++ b/src/remotedesktop.c +@@ -104,9 +104,10 @@ static void + start_done (RemoteDesktopSession *session); + + static gboolean +-start_session (RemoteDesktopSession *session, +- GVariant *selections, +- GError **error); ++start_session (RemoteDesktopSession *session, ++ RemoteDesktopDeviceType device_types, ++ GPtrArray *streams, ++ GError **error); + + static void + cancel_start_session (RemoteDesktopSession *session, +@@ -155,9 +156,10 @@ handle_close (XdpImplRequest *object, + } + + static void +-remote_desktop_dialog_done (GtkWidget *widget, +- int dialog_response, +- GVariant *selections, ++remote_desktop_dialog_done (GtkWidget *widget, ++ int dialog_response, ++ RemoteDesktopDeviceType device_types, ++ GPtrArray *streams, + RemoteDesktopDialogHandle *dialog_handle) + { + int response; +@@ -184,7 +186,7 @@ remote_desktop_dialog_done (GtkWidget *widget, + { + g_autoptr(GError) error = NULL; + +- if (!start_session (dialog_handle->session, selections, &error)) ++ if (!start_session (dialog_handle->session, device_types, streams, &error)) + { + g_warning ("Failed to start session: %s", error->message); + response = 2; +@@ -453,9 +455,9 @@ on_gnome_screen_cast_session_ready (GnomeScreenCastSession *gnome_screen_cast_se + } + + static gboolean +-open_screen_cast_session (RemoteDesktopSession *remote_desktop_session, +- GVariant *source_selections, +- GError **error) ++open_screen_cast_session (RemoteDesktopSession *remote_desktop_session, ++ GPtrArray *streams, ++ GError **error) + { + OrgGnomeMutterRemoteDesktopSession *session_proxy = + remote_desktop_session->mutter_session_proxy; +@@ -478,7 +480,7 @@ open_screen_cast_session (RemoteDesktopSession *remote_desktop_session, + remote_desktop_session); + + if (!gnome_screen_cast_session_record_selections (gnome_screen_cast_session, +- source_selections, ++ streams, + &remote_desktop_session->select.screen_cast, + error)) + return FALSE; +@@ -487,23 +489,19 @@ open_screen_cast_session (RemoteDesktopSession *remote_desktop_session, + } + + static gboolean +-start_session (RemoteDesktopSession *remote_desktop_session, +- GVariant *selections, +- GError **error) ++start_session (RemoteDesktopSession *remote_desktop_session, ++ RemoteDesktopDeviceType device_types, ++ GPtrArray *streams, ++ GError **error) + { + OrgGnomeMutterRemoteDesktopSession *session_proxy; +- RemoteDesktopDeviceType device_types = 0; +- g_autoptr(GVariant) source_selections = NULL; + gboolean need_streams; + +- g_variant_lookup (selections, "selected_device_types", "u", &device_types); + remote_desktop_session->shared.device_types = device_types; + +- if (g_variant_lookup (selections, "selected_screen_cast_sources", "@a(us)", +- &source_selections)) ++ if (streams) + { +- if (!open_screen_cast_session (remote_desktop_session, +- source_selections, error)) ++ if (!open_screen_cast_session (remote_desktop_session, streams, error)) + return FALSE; + + need_streams = TRUE; +diff --git a/src/remotedesktopdialog.c b/src/remotedesktopdialog.c +index a21b008..bae7678 100644 +--- a/src/remotedesktopdialog.c ++++ b/src/remotedesktopdialog.c +@@ -61,9 +61,8 @@ static GQuark quark_device_widget_data; + + G_DEFINE_TYPE (RemoteDesktopDialog, remote_desktop_dialog, GTK_TYPE_WINDOW) + +-static void +-add_device_type_selections (RemoteDesktopDialog *dialog, +- GVariantBuilder *selections_builder) ++static RemoteDesktopDeviceType ++get_selected_device_types (RemoteDesktopDialog *dialog) + { + GList *selected_rows; + GList *l; +@@ -81,43 +80,36 @@ add_device_type_selections (RemoteDesktopDialog *dialog, + } + g_list_free (selected_rows); + +- g_variant_builder_add (selections_builder, "{sv}", +- "selected_device_types", +- g_variant_new_uint32 (selected_device_types)); ++ return selected_device_types; + } + + static void + button_clicked (GtkWidget *button, + RemoteDesktopDialog *dialog) + { ++ RemoteDesktopDeviceType device_types = 0; ++ g_autoptr(GPtrArray) streams = NULL; + int response; +- GVariant *selections; + + gtk_widget_hide (GTK_WIDGET (dialog)); + + if (button == dialog->accept_button) + { +- GVariantBuilder selections_builder; + ScreenCastWidget *screen_cast_widget = + SCREEN_CAST_WIDGET (dialog->screen_cast_widget); + + response = GTK_RESPONSE_OK; +- +- g_variant_builder_init (&selections_builder, G_VARIANT_TYPE_VARDICT); +- +- add_device_type_selections (dialog, &selections_builder); +- if (dialog->screen_cast_enable) +- screen_cast_widget_add_selections (screen_cast_widget, +- &selections_builder); +- selections = g_variant_builder_end (&selections_builder); ++ device_types = get_selected_device_types (dialog); ++ streams = screen_cast_widget_get_selected_streams (screen_cast_widget); + } + else + { + response = GTK_RESPONSE_CANCEL; +- selections = NULL; ++ device_types = 0; ++ streams = NULL; + } + +- g_signal_emit (dialog, signals[DONE], 0, response, selections); ++ g_signal_emit (dialog, signals[DONE], 0, response, device_types, streams); + } + + static void +@@ -356,7 +348,7 @@ remote_desktop_dialog_close_request (GtkWindow *dialog) + { + gtk_widget_hide (GTK_WIDGET (dialog)); + +- g_signal_emit (dialog, signals[DONE], 0, GTK_RESPONSE_CANCEL, NULL); ++ g_signal_emit (dialog, signals[DONE], 0, GTK_RESPONSE_CANCEL, 0, NULL); + + return TRUE; + } +@@ -375,9 +367,10 @@ remote_desktop_dialog_class_init (RemoteDesktopDialogClass *klass) + 0, + NULL, NULL, + NULL, +- G_TYPE_NONE, 2, ++ G_TYPE_NONE, 3, ++ G_TYPE_INT, + G_TYPE_INT, +- G_TYPE_VARIANT); ++ G_TYPE_PTR_ARRAY); + + init_screen_cast_widget (); + +diff --git a/src/screencast.c b/src/screencast.c +index 4ba67aa..2713d26 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -77,11 +77,6 @@ static GnomeScreenCast *gnome_screen_cast; + GType screen_cast_session_get_type (void); + G_DEFINE_TYPE (ScreenCastSession, screen_cast_session, session_get_type ()) + +-static gboolean +-start_session (ScreenCastSession *session, +- GVariant *selections, +- GError **error); +- + static gboolean + is_screen_cast_session (Session *session) + { +@@ -129,10 +124,76 @@ on_request_handle_close_cb (XdpImplRequest *object, + return FALSE; + } + ++static void ++on_gnome_screen_cast_session_ready (GnomeScreenCastSession *gnome_screen_cast_session, ++ ScreenCastSession *screen_cast_session) ++{ ++ GVariantBuilder streams_builder; ++ GVariantBuilder results_builder; ++ ++ g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); ++ g_variant_builder_init (&streams_builder, G_VARIANT_TYPE ("a(ua{sv})")); ++ ++ gnome_screen_cast_session = screen_cast_session->gnome_screen_cast_session; ++ gnome_screen_cast_session_add_stream_properties (gnome_screen_cast_session, ++ &streams_builder); ++ ++ g_variant_builder_add (&results_builder, "{sv}", ++ "streams", ++ g_variant_builder_end (&streams_builder)); ++ ++ xdp_impl_screen_cast_complete_start (XDP_IMPL_SCREEN_CAST (impl), ++ screen_cast_session->start_invocation, 0, ++ g_variant_builder_end (&results_builder)); ++ screen_cast_session->start_invocation = NULL; ++} ++ ++static void ++on_gnome_screen_cast_session_closed (GnomeScreenCastSession *gnome_screen_cast_session, ++ ScreenCastSession *screen_cast_session) ++{ ++ session_close ((Session *)screen_cast_session); ++} ++ ++static gboolean ++start_session (ScreenCastSession *screen_cast_session, ++ GPtrArray *streams, ++ GError **error) ++{ ++ GnomeScreenCastSession *gnome_screen_cast_session; ++ ++ gnome_screen_cast_session = ++ gnome_screen_cast_create_session (gnome_screen_cast, NULL, error); ++ if (!gnome_screen_cast_session) ++ return FALSE; ++ ++ screen_cast_session->gnome_screen_cast_session = gnome_screen_cast_session; ++ ++ screen_cast_session->session_ready_handler_id = ++ g_signal_connect (gnome_screen_cast_session, "ready", ++ G_CALLBACK (on_gnome_screen_cast_session_ready), ++ screen_cast_session); ++ screen_cast_session->session_closed_handler_id = ++ g_signal_connect (gnome_screen_cast_session, "closed", ++ G_CALLBACK (on_gnome_screen_cast_session_closed), ++ screen_cast_session); ++ ++ if (!gnome_screen_cast_session_record_selections (gnome_screen_cast_session, ++ streams, ++ &screen_cast_session->select, ++ error)) ++ return FALSE; ++ ++ if (!gnome_screen_cast_session_start (gnome_screen_cast_session, error)) ++ return FALSE; ++ ++ return TRUE; ++} ++ + static void + on_screen_cast_dialog_done_cb (GtkWidget *widget, + int dialog_response, +- GVariant *selections, ++ GPtrArray *streams, + ScreenCastDialogHandle *dialog_handle) + { + int response; +@@ -159,7 +220,7 @@ on_screen_cast_dialog_done_cb (GtkWidget *widget, + { + g_autoptr(GError) error = NULL; + +- if (!start_session (dialog_handle->session, selections, &error)) ++ if (!start_session (dialog_handle->session, streams, &error)) + { + g_warning ("Failed to start session: %s", error->message); + response = 2; +@@ -234,82 +295,6 @@ create_screen_cast_dialog (ScreenCastSession *session, + return dialog_handle; + } + +-static void +-start_done (ScreenCastSession *screen_cast_session) +-{ +- GnomeScreenCastSession *gnome_screen_cast_session; +- GVariantBuilder streams_builder; +- GVariantBuilder results_builder; +- +- g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); +- g_variant_builder_init (&streams_builder, G_VARIANT_TYPE ("a(ua{sv})")); +- +- gnome_screen_cast_session = screen_cast_session->gnome_screen_cast_session; +- gnome_screen_cast_session_add_stream_properties (gnome_screen_cast_session, +- &streams_builder); +- +- g_variant_builder_add (&results_builder, "{sv}", +- "streams", +- g_variant_builder_end (&streams_builder)); +- +- xdp_impl_screen_cast_complete_start (XDP_IMPL_SCREEN_CAST (impl), +- screen_cast_session->start_invocation, 0, +- g_variant_builder_end (&results_builder)); +- screen_cast_session->start_invocation = NULL; +-} +- +-static void +-on_gnome_screen_cast_session_ready (GnomeScreenCastSession *gnome_screen_cast_session, +- ScreenCastSession *screen_cast_session) +-{ +- start_done (screen_cast_session); +-} +- +-static void +-on_gnome_screen_cast_session_closed (GnomeScreenCastSession *gnome_screen_cast_session, +- ScreenCastSession *screen_cast_session) +-{ +- session_close ((Session *)screen_cast_session); +-} +- +-static gboolean +-start_session (ScreenCastSession *screen_cast_session, +- GVariant *selections, +- GError **error) +-{ +- GnomeScreenCastSession *gnome_screen_cast_session; +- g_autoptr(GVariant) source_selections = NULL; +- +- gnome_screen_cast_session = +- gnome_screen_cast_create_session (gnome_screen_cast, NULL, error); +- if (!gnome_screen_cast_session) +- return FALSE; +- +- screen_cast_session->gnome_screen_cast_session = gnome_screen_cast_session; +- +- screen_cast_session->session_ready_handler_id = +- g_signal_connect (gnome_screen_cast_session, "ready", +- G_CALLBACK (on_gnome_screen_cast_session_ready), +- screen_cast_session); +- screen_cast_session->session_closed_handler_id = +- g_signal_connect (gnome_screen_cast_session, "closed", +- G_CALLBACK (on_gnome_screen_cast_session_closed), +- screen_cast_session); +- +- g_variant_lookup (selections, "selected_screen_cast_sources", "@a(u?)", +- &source_selections); +- if (!gnome_screen_cast_session_record_selections (gnome_screen_cast_session, +- source_selections, +- &screen_cast_session->select, +- error)) +- return FALSE; +- +- if (!gnome_screen_cast_session_start (gnome_screen_cast_session, error)) +- return FALSE; +- +- return TRUE; +-} +- + static gboolean + handle_start (XdpImplScreenCast *object, + GDBusMethodInvocation *invocation, +diff --git a/src/screencast.h b/src/screencast.h +index f5033b2..a9be16b 100644 +--- a/src/screencast.h ++++ b/src/screencast.h +@@ -21,6 +21,9 @@ + #include + #include + ++#include "displaystatetracker.h" ++#include "shellintrospect.h" ++ + typedef enum _ScreenCastSourceType + { + SCREEN_CAST_SOURCE_TYPE_MONITOR = 1, +@@ -42,5 +45,14 @@ typedef struct _ScreenCastSelection + ScreenCastCursorMode cursor_mode; + } ScreenCastSelection; + ++typedef struct ++{ ++ ScreenCastSourceType type; ++ union { ++ Monitor *monitor; ++ Window *window; ++ } data; ++} ScreenCastStreamInfo; ++ + gboolean screen_cast_init (GDBusConnection *connection, + GError **error); +diff --git a/src/screencastdialog.c b/src/screencastdialog.c +index 56d4d49..3e3b064 100644 +--- a/src/screencastdialog.c ++++ b/src/screencastdialog.c +@@ -59,8 +59,8 @@ static void + button_clicked (GtkWidget *button, + ScreenCastDialog *dialog) + { ++ g_autoptr(GPtrArray) streams = NULL; + int response; +- GVariant *selections; + + gtk_widget_hide (GTK_WIDGET (dialog)); + +@@ -68,22 +68,17 @@ button_clicked (GtkWidget *button, + { + ScreenCastWidget *screen_cast_widget = + SCREEN_CAST_WIDGET (dialog->screen_cast_widget); +- GVariantBuilder selections_builder; + + response = GTK_RESPONSE_OK; +- +- g_variant_builder_init (&selections_builder, G_VARIANT_TYPE ("a{sv}")); +- screen_cast_widget_add_selections (screen_cast_widget, +- &selections_builder); +- selections = g_variant_builder_end (&selections_builder); ++ streams = screen_cast_widget_get_selected_streams (screen_cast_widget); + } + else + { + response = GTK_RESPONSE_CANCEL; +- selections = NULL; ++ streams = NULL; + } + +- g_signal_emit (dialog, signals[DONE], 0, response, selections); ++ g_signal_emit (dialog, signals[DONE], 0, response, streams); + } + + static void +@@ -127,7 +122,7 @@ screen_cast_dialog_class_init (ScreenCastDialogClass *klass) + NULL, + G_TYPE_NONE, 2, + G_TYPE_INT, +- G_TYPE_VARIANT); ++ G_TYPE_PTR_ARRAY); + + init_screen_cast_widget (); + +diff --git a/src/screencastwidget.c b/src/screencastwidget.c +index 454c93e..3119245 100644 +--- a/src/screencastwidget.c ++++ b/src/screencastwidget.c +@@ -69,51 +69,6 @@ G_DEFINE_TYPE (ScreenCastWidget, screen_cast_widget, GTK_TYPE_BOX) + * Auxiliary methods + */ + +-static gboolean +-add_selections (ScreenCastWidget *widget, +- GVariantBuilder *source_selections_builder) +-{ +- GList *selected_monitor_rows; +- GList *selected_window_rows; +- GList *l; +- +- selected_monitor_rows = +- gtk_list_box_get_selected_rows (GTK_LIST_BOX (widget->monitor_list)); +- selected_window_rows = +- gtk_list_box_get_selected_rows (GTK_LIST_BOX (widget->window_list)); +- if (!selected_monitor_rows && !selected_window_rows) +- return FALSE; +- +- for (l = selected_monitor_rows; l; l = l->next) +- { +- GtkWidget *monitor_widget = gtk_list_box_row_get_child (l->data); +- Monitor *monitor; +- +- monitor = g_object_get_qdata (G_OBJECT (monitor_widget), +- quark_monitor_widget_data); +- +- g_variant_builder_add (source_selections_builder, "(us)", +- SCREEN_CAST_SOURCE_TYPE_MONITOR, +- monitor_get_connector (monitor)); +- } +- g_list_free (selected_monitor_rows); +- for (l = selected_window_rows; l; l = l->next) +- { +- GtkWidget *window_widget = gtk_list_box_row_get_child (l->data); +- Window *window; +- +- window = g_object_get_qdata (G_OBJECT (window_widget), +- quark_window_widget_data); +- +- g_variant_builder_add (source_selections_builder, "(ut)", +- SCREEN_CAST_SOURCE_TYPE_WINDOW, +- window_get_id (window)); +- } +- g_list_free (selected_window_rows); +- +- return TRUE; +-} +- + static GtkWidget * + create_window_widget (Window *window) + { +@@ -571,25 +526,6 @@ init_screen_cast_widget (void) + g_type_ensure (screen_cast_widget_get_type ()); + } + +-void +-screen_cast_widget_add_selections (ScreenCastWidget *widget, +- GVariantBuilder *selections_builder) +-{ +- GVariantBuilder source_selections_builder; +- +- g_variant_builder_init (&source_selections_builder, G_VARIANT_TYPE ("a(u?)")); +- if (!add_selections (widget, &source_selections_builder)) +- { +- g_variant_builder_clear (&source_selections_builder); +- } +- else +- { +- g_variant_builder_add (selections_builder, "{sv}", +- "selected_screen_cast_sources", +- g_variant_builder_end (&source_selections_builder)); +- } +-} +- + void + screen_cast_widget_set_app_id (ScreenCastWidget *widget, + const char *app_id) +@@ -649,3 +585,53 @@ screen_cast_widget_set_source_types (ScreenCastWidget *screen_cast_widget, + if (__builtin_popcount (source_types) > 1) + gtk_widget_show (screen_cast_widget->source_type_switcher); + } ++ ++GPtrArray * ++screen_cast_widget_get_selected_streams (ScreenCastWidget *self) ++{ ++ ScreenCastStreamInfo *info; ++ g_autoptr(GPtrArray) streams = NULL; ++ g_autoptr(GList) selected_monitor_rows = NULL; ++ g_autoptr(GList) selected_window_rows = NULL; ++ GList *l; ++ ++ streams = g_ptr_array_new_with_free_func (g_free); ++ ++ selected_monitor_rows = ++ gtk_list_box_get_selected_rows (GTK_LIST_BOX (self->monitor_list)); ++ selected_window_rows = ++ gtk_list_box_get_selected_rows (GTK_LIST_BOX (self->window_list)); ++ ++ if (!selected_monitor_rows && !selected_window_rows) ++ return g_steal_pointer (&streams); ++ ++ for (l = selected_monitor_rows; l; l = l->next) ++ { ++ GtkWidget *monitor_widget = gtk_list_box_row_get_child (l->data); ++ Monitor *monitor; ++ ++ monitor = g_object_get_qdata (G_OBJECT (monitor_widget), ++ quark_monitor_widget_data); ++ ++ info = g_new0 (ScreenCastStreamInfo, 1); ++ info->type = SCREEN_CAST_SOURCE_TYPE_MONITOR; ++ info->data.monitor = monitor; ++ g_ptr_array_add (streams, info); ++ } ++ ++ for (l = selected_window_rows; l; l = l->next) ++ { ++ GtkWidget *window_widget = gtk_list_box_row_get_child (l->data); ++ Window *window; ++ ++ window = g_object_get_qdata (G_OBJECT (window_widget), ++ quark_window_widget_data); ++ ++ info = g_new0 (ScreenCastStreamInfo, 1); ++ info->type = SCREEN_CAST_SOURCE_TYPE_WINDOW; ++ info->data.window = window; ++ g_ptr_array_add (streams, info); ++ } ++ ++ return g_steal_pointer (&streams); ++} +diff --git a/src/screencastwidget.h b/src/screencastwidget.h +index 34360a3..ad95903 100644 +--- a/src/screencastwidget.h ++++ b/src/screencastwidget.h +@@ -39,5 +39,4 @@ void screen_cast_widget_set_allow_multiple (ScreenCastWidget *widget, + void screen_cast_widget_set_source_types (ScreenCastWidget *screen_cast_widget, + ScreenCastSourceType source_types); + +-void screen_cast_widget_add_selections (ScreenCastWidget *widget, +- GVariantBuilder *selections_builder); ++GPtrArray *screen_cast_widget_get_selected_streams (ScreenCastWidget *self); diff --git a/0005-displaystatetracker-add-match-string.patch b/0005-displaystatetracker-add-match-string.patch new file mode 100644 index 0000000..7125697 --- /dev/null +++ b/0005-displaystatetracker-add-match-string.patch @@ -0,0 +1,90 @@ +From 36594e191980d84ca77ba09faddf3f160a9839cf Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Tue, 28 Sep 2021 21:00:35 -0300 +Subject: [PATCH] displaystatetracker: Add match string + +The monitor's match string is a unique string that can be used to compare +monitors and check for equality. It's composed of the product, vendor, and +serial numbers. It'll be used by the next commit to restore sessions. +--- + src/displaystatetracker.c | 18 +++++++++++++++--- + src/displaystatetracker.h | 2 ++ + 2 files changed, 17 insertions(+), 3 deletions(-) + +diff --git a/src/displaystatetracker.c b/src/displaystatetracker.c +index 6affd8a..fc9109e 100644 +--- a/src/displaystatetracker.c ++++ b/src/displaystatetracker.c +@@ -35,6 +35,7 @@ static guint signals[N_SIGNALS]; + typedef struct _Monitor + { + char *connector; ++ char *match_string; + char *display_name; + } Monitor; + +@@ -65,6 +66,7 @@ static void + monitor_free (Monitor *monitor) + { + g_free (monitor->connector); ++ g_free (monitor->match_string); + g_free (monitor->display_name); + g_free (monitor); + } +@@ -75,6 +77,12 @@ monitor_get_connector (Monitor *monitor) + return monitor->connector; + } + ++const char * ++monitor_get_match_string (Monitor *monitor) ++{ ++ return monitor->match_string; ++} ++ + const char * + monitor_get_display_name (Monitor *monitor) + { +@@ -109,6 +117,9 @@ generate_monitors (DisplayStateTracker *tracker, + g_variant_iter_init (&monitors_iter, monitors); + while ((monitor_variant = g_variant_iter_next_value (&monitors_iter))) + { ++ g_autofree char *vendor = NULL; ++ g_autofree char *product = NULL; ++ g_autofree char *serial = NULL; + Monitor *monitor; + char *connector; + char *display_name; +@@ -116,9 +127,9 @@ generate_monitors (DisplayStateTracker *tracker, + + g_variant_get (monitor_variant, "((ssss)a(siiddada{sv})@a{sv})", + &connector, +- NULL /* vendor */, +- NULL /* product */, +- NULL /* serial */, ++ &vendor, ++ &product, ++ &serial, + NULL /* modes */, + &properties); + +@@ -128,6 +139,7 @@ generate_monitors (DisplayStateTracker *tracker, + monitor = g_new0 (Monitor, 1); + *monitor = (Monitor) { + .connector = connector, ++ .match_string = g_strdup_printf ("%s:%s:%s", vendor, product, serial), + .display_name = display_name + }; + +diff --git a/src/displaystatetracker.h b/src/displaystatetracker.h +index 54c63fa..1c3a7dc 100644 +--- a/src/displaystatetracker.h ++++ b/src/displaystatetracker.h +@@ -28,6 +28,8 @@ G_DECLARE_FINAL_TYPE (DisplayStateTracker, display_state_tracker, + + const char * monitor_get_connector (Monitor *monitor); + ++const char * monitor_get_match_string (Monitor *monitor); ++ + const char * monitor_get_display_name (Monitor *monitor); + + GList * logical_monitor_get_monitors (LogicalMonitor *logical_monitor); diff --git a/0006-implement-screencast-stream-restoration.patch b/0006-implement-screencast-stream-restoration.patch new file mode 100644 index 0000000..dbb1d36 --- /dev/null +++ b/0006-implement-screencast-stream-restoration.patch @@ -0,0 +1,724 @@ +From af25561e0f4e5aa2c3460ce7448b0c580f28532a Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Tue, 28 Sep 2021 21:04:28 -0300 +Subject: [PATCH] Implement screencast stream restoration + +Handle receiving 'restore_data' and 'persist_mode'. + +For monitors, use the match string introduced by the previous +commit to identify individual monitors. For windows, do a strict +check on the app id, and a best-match approach to window titles. +Virtual monitors don't have any particular data attached to them, +so they are merely stored as "virtual" and restored. +--- + src/screencast.c | 327 +++++++++++++++++++++++++++++++++++++++- + src/screencast.h | 7 + + src/screencastdialog.c | 14 +- + src/screencastdialog.h | 5 +- + src/screencastwidget.c | 23 +++ + src/screencastwidget.h | 6 + + src/screencastwidget.ui | 8 + + src/utils.c | 77 ++++++++++ + src/utils.h | 3 + + 9 files changed, 456 insertions(+), 14 deletions(-) + +diff --git a/src/screencast.c b/src/screencast.c +index 2713d26..55a3e5c 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -33,8 +33,15 @@ + #include "externalwindow.h" + #include "request.h" + #include "session.h" ++#include "shellintrospect.h" + #include "utils.h" + ++#define RESTORE_FORMAT_VERSION 1 ++#define RESTORE_VARIANT_TYPE "(xxa(uuv))" ++#define MONITOR_TYPE "s" ++#define WINDOW_TYPE "(ss)" ++#define VIRTUAL_TYPE "b" ++ + typedef struct _ScreenCastDialogHandle ScreenCastDialogHandle; + + typedef struct _ScreenCastSession +@@ -49,6 +56,13 @@ typedef struct _ScreenCastSession + + ScreenCastSelection select; + ++ ScreenCastPersistMode persist_mode; ++ GPtrArray *streams_to_restore; ++ struct { ++ GVariant *data; ++ int64_t creation_time; ++ } restored; ++ + GDBusMethodInvocation *start_invocation; + ScreenCastDialogHandle *dialog_handle; + } ScreenCastSession; +@@ -99,6 +113,68 @@ screen_cast_dialog_handle_close (ScreenCastDialogHandle *dialog_handle) + screen_cast_dialog_handle_free (dialog_handle); + } + ++static GVariant * ++serialize_streams_as_restore_data (ScreenCastSession *screen_cast_session, ++ GPtrArray *streams) ++{ ++ GVariantBuilder restore_data_builder; ++ GVariantBuilder impl_builder; ++ int64_t creation_time; ++ int64_t last_used_time; ++ guint i; ++ ++ if (!streams || streams->len == 0) ++ return NULL; ++ ++ last_used_time = g_get_real_time (); ++ if (screen_cast_session->restored.creation_time != -1) ++ creation_time = screen_cast_session->restored.creation_time; ++ else ++ creation_time = g_get_real_time (); ++ ++ g_variant_builder_init (&impl_builder, G_VARIANT_TYPE (RESTORE_VARIANT_TYPE)); ++ g_variant_builder_add (&impl_builder, "x", creation_time); ++ g_variant_builder_add (&impl_builder, "x", last_used_time); ++ ++ g_variant_builder_open (&impl_builder, G_VARIANT_TYPE ("a(uuv)")); ++ for (i = 0; i < streams->len; i++) ++ { ++ ScreenCastStreamInfo *info = g_ptr_array_index (streams, i); ++ GVariant *stream_variant; ++ Monitor *monitor; ++ Window *window; ++ ++ switch (info->type) ++ { ++ case SCREEN_CAST_SOURCE_TYPE_MONITOR: ++ monitor = info->data.monitor; ++ stream_variant = g_variant_new (MONITOR_TYPE, ++ monitor_get_match_string (monitor)); ++ break; ++ ++ case SCREEN_CAST_SOURCE_TYPE_WINDOW: ++ window = info->data.window; ++ stream_variant = g_variant_new (WINDOW_TYPE, ++ window_get_app_id (window), ++ window_get_title (window)); ++ break; ++ } ++ ++ g_variant_builder_add (&impl_builder, ++ "(uuv)", ++ i, ++ info->type, ++ stream_variant); ++ } ++ g_variant_builder_close (&impl_builder); ++ ++ g_variant_builder_init (&restore_data_builder, G_VARIANT_TYPE ("(suv)")); ++ g_variant_builder_add (&restore_data_builder, "s", "GNOME"); ++ g_variant_builder_add (&restore_data_builder, "u", RESTORE_FORMAT_VERSION); ++ g_variant_builder_add (&restore_data_builder, "v", g_variant_builder_end (&impl_builder)); ++ return g_variant_builder_end (&restore_data_builder); ++} ++ + static void + cancel_start_session (ScreenCastSession *screen_cast_session, + int response) +@@ -142,6 +218,22 @@ on_gnome_screen_cast_session_ready (GnomeScreenCastSession *gnome_screen_cast_se + "streams", + g_variant_builder_end (&streams_builder)); + ++ if (screen_cast_session->persist_mode != SCREEN_CAST_PERSIST_MODE_NONE) ++ { ++ g_autoptr(GPtrArray) streams = g_steal_pointer (&screen_cast_session->streams_to_restore); ++ GVariant *restore_data; ++ ++ restore_data = serialize_streams_as_restore_data (screen_cast_session, streams); ++ ++ if (restore_data) ++ { ++ g_variant_builder_add (&results_builder, "{sv}", "persist_mode", ++ g_variant_new_uint32 (screen_cast_session->persist_mode)); ++ g_variant_builder_add (&results_builder, "{sv}", "restore_data", ++ g_variant_new_variant (restore_data)); ++ } ++ } ++ + xdp_impl_screen_cast_complete_start (XDP_IMPL_SCREEN_CAST (impl), + screen_cast_session->start_invocation, 0, + g_variant_builder_end (&results_builder)); +@@ -178,6 +270,9 @@ start_session (ScreenCastSession *screen_cast_session, + G_CALLBACK (on_gnome_screen_cast_session_closed), + screen_cast_session); + ++ if (screen_cast_session->persist_mode != SCREEN_CAST_PERSIST_MODE_NONE) ++ screen_cast_session->streams_to_restore = g_ptr_array_ref (streams); ++ + if (!gnome_screen_cast_session_record_selections (gnome_screen_cast_session, + streams, + &screen_cast_session->select, +@@ -193,6 +288,7 @@ start_session (ScreenCastSession *screen_cast_session, + static void + on_screen_cast_dialog_done_cb (GtkWidget *widget, + int dialog_response, ++ ScreenCastPersistMode persist_mode, + GPtrArray *streams, + ScreenCastDialogHandle *dialog_handle) + { +@@ -218,8 +314,12 @@ on_screen_cast_dialog_done_cb (GtkWidget *widget, + + if (response == 0) + { ++ ScreenCastSession *screen_cast_session = dialog_handle->session; + g_autoptr(GError) error = NULL; + ++ screen_cast_session->persist_mode = MIN (screen_cast_session->persist_mode, ++ persist_mode); ++ + if (!start_session (dialog_handle->session, streams, &error)) + { + g_warning ("Failed to start session: %s", error->message); +@@ -269,7 +369,8 @@ create_screen_cast_dialog (ScreenCastSession *session, + g_object_ref_sink (fake_parent); + + dialog = GTK_WIDGET (screen_cast_dialog_new (request->app_id, +- &session->select)); ++ &session->select, ++ session->persist_mode)); + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fake_parent)); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + +@@ -295,6 +396,161 @@ create_screen_cast_dialog (ScreenCastSession *session, + return dialog_handle; + } + ++static Monitor * ++find_monitor_by_string (const char *monitor_string) ++{ ++ DisplayStateTracker *display_state_tracker = display_state_tracker_get (); ++ GList *l; ++ ++ for (l = display_state_tracker_get_logical_monitors (display_state_tracker); ++ l; ++ l = l->next) ++ { ++ LogicalMonitor *logical_monitor = l->data; ++ GList *monitors; ++ ++ for (monitors = logical_monitor_get_monitors (logical_monitor); ++ monitors; ++ monitors = monitors->next) ++ { ++ Monitor *monitor = monitors->data; ++ ++ if (g_strcmp0 (monitor_get_match_string (monitor), monitor_string) == 0) ++ return monitor; ++ } ++ } ++ ++ return NULL; ++} ++ ++static Window * ++find_best_window_by_app_id_and_title (const char *app_id, ++ const char *title) ++{ ++ ShellIntrospect *shell_introspect = shell_introspect_get (); ++ Window *best_match; ++ glong best_match_distance; ++ GList *l; ++ ++ best_match = NULL; ++ best_match_distance = G_MAXLONG; ++ ++ for (l = shell_introspect_get_windows (shell_introspect); l; l = l->next) ++ { ++ Window *window = l->data; ++ glong distance; ++ ++ if (g_strcmp0 (window_get_app_id (window), app_id) != 0) ++ continue; ++ ++ distance = str_distance (window_get_title (window), title); ++ ++ if (distance == 0) ++ return window; ++ ++ if (distance < best_match_distance) ++ { ++ best_match = window; ++ best_match_distance = distance; ++ } ++ } ++ ++ return best_match; ++} ++ ++static gboolean ++restore_stream_from_data (ScreenCastSession *screen_cast_session) ++ ++{ ++ ScreenCastStreamInfo *info; ++ g_autoptr(GVariantIter) array_iter = NULL; ++ g_autoptr(GPtrArray) streams = NULL; ++ g_autoptr(GError) error = NULL; ++ ScreenCastSourceType source_type; ++ GVariant *data; ++ uint32_t id; ++ int64_t creation_time; ++ int64_t last_used_time; ++ ++ if (!screen_cast_session->restored.data) ++ return FALSE; ++ ++ streams = g_ptr_array_new_with_free_func (g_free); ++ ++ g_variant_get (screen_cast_session->restored.data, ++ RESTORE_VARIANT_TYPE, ++ &creation_time, ++ &last_used_time, ++ &array_iter); ++ ++ while (g_variant_iter_next (array_iter, "(uuv)", &id, &source_type, &data)) ++ { ++ switch (source_type) ++ { ++ case SCREEN_CAST_SOURCE_TYPE_MONITOR: ++ { ++ if (!(screen_cast_session->select.source_types & SCREEN_CAST_SOURCE_TYPE_MONITOR) || ++ !g_variant_check_format_string (data, MONITOR_TYPE, FALSE)) ++ goto fail; ++ ++ const char *match_string = g_variant_get_string (data, NULL); ++ Monitor *monitor = find_monitor_by_string (match_string); ++ ++ if (!monitor) ++ goto fail; ++ ++ info = g_new0 (ScreenCastStreamInfo, 1); ++ info->type = SCREEN_CAST_SOURCE_TYPE_MONITOR; ++ info->data.monitor = monitor; ++ g_ptr_array_add (streams, info); ++ } ++ break; ++ ++ case SCREEN_CAST_SOURCE_TYPE_WINDOW: ++ { ++ if (!(screen_cast_session->select.source_types & SCREEN_CAST_SOURCE_TYPE_WINDOW) || ++ !g_variant_check_format_string (data, WINDOW_TYPE, FALSE)) ++ goto fail; ++ ++ const char *app_id = NULL; ++ const char *title = NULL; ++ Window *window; ++ ++ g_variant_get (data, "(&s&s)", &app_id, &title); ++ ++ window = find_best_window_by_app_id_and_title (app_id, title); ++ ++ if (!window) ++ goto fail; ++ ++ info = g_new0 (ScreenCastStreamInfo, 1); ++ info->type = SCREEN_CAST_SOURCE_TYPE_WINDOW; ++ info->data.window = window; ++ g_ptr_array_add (streams, info); ++ } ++ break; ++ ++ default: ++ goto fail; ++ } ++ } ++ ++ screen_cast_session->restored.creation_time = creation_time; ++ ++ start_session (screen_cast_session, streams, &error); ++ ++ if (error) ++ { ++ g_warning ("Error restoring stream from session: %s", error->message); ++ return FALSE; ++ } ++ ++ return TRUE; ++ ++fail: ++ return FALSE; ++} ++ + static gboolean + handle_start (XdpImplScreenCast *object, + GDBusMethodInvocation *invocation, +@@ -307,7 +563,6 @@ handle_start (XdpImplScreenCast *object, + const char *sender; + g_autoptr(Request) request = NULL; + ScreenCastSession *screen_cast_session; +- ScreenCastDialogHandle *dialog_handle; + GVariantBuilder results_builder; + + sender = g_dbus_method_invocation_get_sender (invocation); +@@ -329,14 +584,19 @@ handle_start (XdpImplScreenCast *object, + goto err; + } + +- dialog_handle = create_screen_cast_dialog (screen_cast_session, +- invocation, +- request, +- arg_parent_window); ++ screen_cast_session->start_invocation = invocation; + ++ if (!restore_stream_from_data (screen_cast_session)) ++ { ++ ScreenCastDialogHandle *dialog_handle; + +- screen_cast_session->start_invocation = invocation; +- screen_cast_session->dialog_handle = dialog_handle; ++ dialog_handle = create_screen_cast_dialog (screen_cast_session, ++ invocation, ++ request, ++ arg_parent_window); ++ ++ screen_cast_session->dialog_handle = dialog_handle; ++ } + + return TRUE; + +@@ -356,6 +616,9 @@ handle_select_sources (XdpImplScreenCast *object, + const char *arg_app_id, + GVariant *arg_options) + { ++ g_autofree gchar *provider = NULL; ++ g_autoptr(GVariant) restore_data = NULL; ++ ScreenCastSession *screen_cast_session; + Session *session; + int response; + uint32_t types; +@@ -364,6 +627,7 @@ handle_select_sources (XdpImplScreenCast *object, + ScreenCastSelection select; + GVariantBuilder results_builder; + GVariant *results; ++ uint32_t version; + + session = lookup_session (arg_session_handle); + if (!session) +@@ -427,6 +691,21 @@ handle_select_sources (XdpImplScreenCast *object, + response = 2; + } + ++ screen_cast_session = (ScreenCastSession *)session; ++ g_variant_lookup (arg_options, "persist_mode", "u", &screen_cast_session->persist_mode); ++ ++ if (g_variant_lookup (arg_options, "restore_data", "(suv)", &provider, &version, &restore_data)) ++ { ++ if (!g_variant_check_format_string (restore_data, "(suv)", FALSE)) ++ { ++ g_warning ("Cannot parse restore data, ignoring"); ++ goto out; ++ } ++ ++ if (g_strcmp0 (provider, "GNOME") == 0 && version == RESTORE_FORMAT_VERSION) ++ screen_cast_session->restored.data = g_variant_ref (restore_data); ++ } ++ + out: + g_variant_builder_init (&results_builder, G_VARIANT_TYPE_VARDICT); + results = g_variant_builder_end (&results_builder); +@@ -567,6 +846,8 @@ screen_cast_session_finalize (GObject *object) + { + ScreenCastSession *screen_cast_session = (ScreenCastSession *)object; + ++ g_clear_pointer (&screen_cast_session->streams_to_restore, g_ptr_array_unref); ++ g_clear_pointer (&screen_cast_session->restored.data, g_variant_unref); + g_clear_object (&screen_cast_session->gnome_screen_cast_session); + + G_OBJECT_CLASS (screen_cast_session_parent_class)->finalize (object); +@@ -575,6 +856,8 @@ screen_cast_session_finalize (GObject *object) + static void + screen_cast_session_init (ScreenCastSession *screen_cast_session) + { ++ screen_cast_session->persist_mode = SCREEN_CAST_PERSIST_MODE_NONE; ++ screen_cast_session->restored.creation_time = -1; + } + + static void +@@ -594,6 +877,14 @@ gboolean + screen_cast_init (GDBusConnection *connection, + GError **error) + { ++ /* ++ * Ensure ShellIntrospect and DisplayStateTracker are initialized before ++ * any screencast session is created to avoid race conditions when restoring ++ * previous streams. ++ */ ++ display_state_tracker_get (); ++ shell_introspect_get (); ++ + impl_connection = connection; + gnome_screen_cast = gnome_screen_cast_new (connection); + +diff --git a/src/screencast.h b/src/screencast.h +index a9be16b..d78066e 100644 +--- a/src/screencast.h ++++ b/src/screencast.h +@@ -38,6 +38,13 @@ typedef enum _ScreenCastCursorMode + SCREEN_CAST_CURSOR_MODE_METADATA = 4, + } ScreenCastCursorMode; + ++typedef enum _ScreenCastPersistMode ++{ ++ SCREEN_CAST_PERSIST_MODE_NONE = 0, ++ SCREEN_CAST_PERSIST_MODE_TRANSIENT = 1, ++ SCREEN_CAST_PERSIST_MODE_PERSISTENT = 2, ++} ScreenCastPersistMode; ++ + typedef struct _ScreenCastSelection + { + gboolean multiple; +diff --git a/src/screencastdialog.c b/src/screencastdialog.c +index 3e3b064..d80329e 100644 +--- a/src/screencastdialog.c ++++ b/src/screencastdialog.c +@@ -59,6 +59,7 @@ static void + button_clicked (GtkWidget *button, + ScreenCastDialog *dialog) + { ++ ScreenCastPersistMode persist_mode; + g_autoptr(GPtrArray) streams = NULL; + int response; + +@@ -71,14 +72,16 @@ button_clicked (GtkWidget *button, + + response = GTK_RESPONSE_OK; + streams = screen_cast_widget_get_selected_streams (screen_cast_widget); ++ persist_mode = screen_cast_widget_get_persist_mode (screen_cast_widget); + } + else + { + response = GTK_RESPONSE_CANCEL; ++ persist_mode = SCREEN_CAST_PERSIST_MODE_NONE; + streams = NULL; + } + +- g_signal_emit (dialog, signals[DONE], 0, response, streams); ++ g_signal_emit (dialog, signals[DONE], 0, response, persist_mode, streams); + } + + static void +@@ -120,7 +123,8 @@ screen_cast_dialog_class_init (ScreenCastDialogClass *klass) + 0, + NULL, NULL, + NULL, +- G_TYPE_NONE, 2, ++ G_TYPE_NONE, 3, ++ G_TYPE_INT, + G_TYPE_INT, + G_TYPE_PTR_ARRAY); + +@@ -143,8 +147,9 @@ screen_cast_dialog_init (ScreenCastDialog *dialog) + } + + ScreenCastDialog * +-screen_cast_dialog_new (const char *app_id, +- ScreenCastSelection *select) ++screen_cast_dialog_new (const char *app_id, ++ ScreenCastSelection *select, ++ ScreenCastPersistMode persist_mode) + { + ScreenCastDialog *dialog; + ScreenCastWidget *screen_cast_widget; +@@ -155,6 +160,7 @@ screen_cast_dialog_new (const char *app_id, + screen_cast_widget_set_allow_multiple (screen_cast_widget, select->multiple); + screen_cast_widget_set_source_types (screen_cast_widget, + select->source_types); ++ screen_cast_widget_set_persist_mode (screen_cast_widget, persist_mode); + + return dialog; + } +diff --git a/src/screencastdialog.h b/src/screencastdialog.h +index 1fca470..c132ecf 100644 +--- a/src/screencastdialog.h ++++ b/src/screencastdialog.h +@@ -26,5 +26,6 @@ + G_DECLARE_FINAL_TYPE (ScreenCastDialog, screen_cast_dialog, + SCREEN_CAST, DIALOG, GtkWindow) + +-ScreenCastDialog * screen_cast_dialog_new (const char *app_id, +- ScreenCastSelection *select); ++ScreenCastDialog * screen_cast_dialog_new (const char *app_id, ++ ScreenCastSelection *select, ++ ScreenCastPersistMode persist_mode); +diff --git a/src/screencastwidget.c b/src/screencastwidget.c +index 3119245..c100ad9 100644 +--- a/src/screencastwidget.c ++++ b/src/screencastwidget.c +@@ -51,6 +51,9 @@ struct _ScreenCastWidget + GtkWidget *window_list; + GtkWidget *window_list_scrolled; + ++ GtkCheckButton *persist_check; ++ ScreenCastPersistMode persist_mode; ++ + DisplayStateTracker *display_state_tracker; + gulong monitors_changed_handler_id; + +@@ -454,6 +457,7 @@ screen_cast_widget_class_init (ScreenCastWidgetClass *klass) + G_TYPE_BOOLEAN); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/portal/desktop/gnome/screencastwidget.ui"); ++ gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, persist_check); + gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, source_type_switcher); + gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, source_type); + gtk_widget_class_bind_template_child (widget_class, ScreenCastWidget, monitor_selection); +@@ -635,3 +639,22 @@ screen_cast_widget_get_selected_streams (ScreenCastWidget *self) + + return g_steal_pointer (&streams); + } ++ ++void ++screen_cast_widget_set_persist_mode (ScreenCastWidget *screen_cast_widget, ++ ScreenCastPersistMode persist_mode) ++{ ++ screen_cast_widget->persist_mode = persist_mode; ++ ++ gtk_widget_set_visible (GTK_WIDGET (screen_cast_widget->persist_check), ++ persist_mode != SCREEN_CAST_PERSIST_MODE_NONE); ++} ++ ++ScreenCastPersistMode ++screen_cast_widget_get_persist_mode (ScreenCastWidget *screen_cast_widget) ++{ ++ if (!gtk_check_button_get_active (screen_cast_widget->persist_check)) ++ return SCREEN_CAST_PERSIST_MODE_NONE; ++ ++ return screen_cast_widget->persist_mode; ++} +diff --git a/src/screencastwidget.h b/src/screencastwidget.h +index ad95903..a963f99 100644 +--- a/src/screencastwidget.h ++++ b/src/screencastwidget.h +@@ -40,3 +40,9 @@ void screen_cast_widget_set_source_types (ScreenCastWidget *screen_cast_widg + ScreenCastSourceType source_types); + + GPtrArray *screen_cast_widget_get_selected_streams (ScreenCastWidget *self); ++ ++void screen_cast_widget_set_persist_mode (ScreenCastWidget *screen_cast_widget, ++ ScreenCastPersistMode persist_mode); ++ ++ScreenCastPersistMode ++screen_cast_widget_get_persist_mode (ScreenCastWidget *screen_cast_widget); +diff --git a/src/screencastwidget.ui b/src/screencastwidget.ui +index fb83b94..0a9028e 100644 +--- a/src/screencastwidget.ui ++++ b/src/screencastwidget.ui +@@ -152,5 +152,13 @@ + + + ++ ++ ++ ++ ++ True ++ Remember this selection ++ ++ + + +diff --git a/src/utils.c b/src/utils.c +index b7dd472..5e0485c 100644 +--- a/src/utils.c ++++ b/src/utils.c +@@ -45,3 +45,80 @@ xdg_desktop_portal_error_quark (void) + G_N_ELEMENTS (xdg_desktop_portal_error_entries)); + return (GQuark) quark_volatile; + } ++ ++glong ++str_distance (const char *a, ++ const char *b) ++{ ++ g_autofree gint *v0 = NULL; ++ g_autofree gint *v1 = NULL; ++ const gchar *s; ++ const gchar *t; ++ gunichar sc; ++ gunichar tc; ++ glong b_char_len; ++ glong cost; ++ glong i; ++ glong j; ++ ++ /* ++ * Handle degenerate cases. ++ */ ++ if (g_strcmp0 (a, b) == 0) ++ return 0; ++ else if (!*a) ++ return g_utf8_strlen (a, -1); ++ else if (!*b) ++ return g_utf8_strlen (a, -1); ++ ++ b_char_len = g_utf8_strlen (b, -1); ++ ++ /* ++ * Create two vectors to hold our states. ++ */ ++ ++ v0 = g_new0 (gint, b_char_len + 1); ++ v1 = g_new0 (gint, b_char_len + 1); ++ ++ /* ++ * initialize v0 (the previous row of distances). ++ * this row is A[0][i]: edit distance for an empty a. ++ * the distance is just the number of characters to delete from b. ++ */ ++ for (i = 0; i < b_char_len + 1; i++) ++ v0[i] = i; ++ ++ for (i = 0, s = a; s && *s; i++, s = g_utf8_next_char(s)) ++ { ++ /* ++ * Calculate v1 (current row distances) from the previous row v0. ++ */ ++ ++ sc = g_utf8_get_char(s); ++ ++ /* ++ * first element of v1 is A[i+1][0] ++ * ++ * edit distance is delete (i+1) chars from a to match empty ++ * b. ++ */ ++ v1[0] = i + 1; ++ ++ /* ++ * use formula to fill in the rest of the row. ++ */ ++ for (j = 0, t = b; t && *t; j++, t = g_utf8_next_char(t)) ++ { ++ tc = g_utf8_get_char(t); ++ cost = (sc == tc) ? 0 : 1; ++ v1[j+1] = MIN (v1[j] + 1, MIN (v0[j+1] + 1, v0[j] + cost)); ++ } ++ ++ /* ++ * copy v1 (current row) to v0 (previous row) for next iteration. ++ */ ++ memcpy (v0, v1, sizeof(gint) * b_char_len); ++ } ++ ++ return v1[b_char_len]; ++} +diff --git a/src/utils.h b/src/utils.h +index 5fdfda9..fa3f1b0 100644 +--- a/src/utils.h ++++ b/src/utils.h +@@ -37,3 +37,6 @@ typedef enum { + #define XDG_DESKTOP_PORTAL_ERROR xdg_desktop_portal_error_quark () + + GQuark xdg_desktop_portal_error_quark (void); ++ ++glong str_distance (const char *a, ++ const char *b); diff --git a/0007-screencast-pass-stream-ids.patch b/0007-screencast-pass-stream-ids.patch new file mode 100644 index 0000000..b8292e0 --- /dev/null +++ b/0007-screencast-pass-stream-ids.patch @@ -0,0 +1,166 @@ +From c865ffdb24d1a945f36a1bf873fe0900cbbade14 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Thu, 11 Nov 2021 00:05:27 -0300 +Subject: [PATCH] screencast: Pass stream ids + +The stream id is any string that can uniquely +identify a stream within a screencast session. +Add that to the list of stream properties. + +For now, only use a simple positional integer +as id. +--- + src/gnomescreencast.c | 21 ++++++++++++++++++--- + src/screencast.c | 5 ++++- + src/screencast.h | 1 + + src/screencastwidget.c | 4 ++++ + 4 files changed, 27 insertions(+), 4 deletions(-) + +diff --git a/src/gnomescreencast.c b/src/gnomescreencast.c +index 3f8539c..482fcd0 100644 +--- a/src/gnomescreencast.c ++++ b/src/gnomescreencast.c +@@ -59,6 +59,7 @@ typedef struct _GnomeScreenCastStream + + ScreenCastSourceType source_type; + ++ uint32_t id; + char *path; + OrgGnomeMutterScreenCastStream *proxy; + +@@ -219,6 +220,7 @@ void + gnome_screen_cast_session_add_stream_properties (GnomeScreenCastSession *gnome_screen_cast_session, + GVariantBuilder *streams_builder) + { ++ char id[64] = { 0, }; + GList *streams; + GList *l; + +@@ -234,6 +236,10 @@ gnome_screen_cast_session_add_stream_properties (GnomeScreenCastSession *gnome_s + + g_variant_builder_init (&stream_properties_builder, G_VARIANT_TYPE_VARDICT); + ++ g_snprintf (id, G_N_ELEMENTS (id), "%u", stream->id); ++ g_variant_builder_add (&stream_properties_builder, "{sv}", ++ "id", ++ g_variant_new ("s", id)); + g_variant_builder_add (&stream_properties_builder, "{sv}", + "source_type", + g_variant_new ("u", stream->source_type)); +@@ -275,6 +281,7 @@ cursor_mode_to_gnome_cursor_mode (ScreenCastCursorMode cursor_mode) + + static gboolean + gnome_screen_cast_session_record_window (GnomeScreenCastSession *gnome_screen_cast_session, ++ uint32_t id, + Window *window, + ScreenCastSelection *select, + GError **error) +@@ -327,6 +334,7 @@ gnome_screen_cast_session_record_window (GnomeScreenCastSession *gnome_screen_c + stream->session = gnome_screen_cast_session; + stream->path = g_strdup (stream_path); + stream->proxy = stream_proxy; ++ stream->id = id; + + parameters = org_gnome_mutter_screen_cast_stream_get_parameters (stream->proxy); + if (parameters) +@@ -357,6 +365,7 @@ gnome_screen_cast_session_record_window (GnomeScreenCastSession *gnome_screen_c + + static gboolean + gnome_screen_cast_session_record_monitor (GnomeScreenCastSession *gnome_screen_cast_session, ++ uint32_t id, + Monitor *monitor, + ScreenCastSelection *select, + GError **error) +@@ -409,6 +418,7 @@ gnome_screen_cast_session_record_monitor (GnomeScreenCastSession *gnome_screen_ + stream->session = gnome_screen_cast_session; + stream->path = g_strdup (stream_path); + stream->proxy = stream_proxy; ++ stream->id = id; + + parameters = org_gnome_mutter_screen_cast_stream_get_parameters (stream->proxy); + if (parameters) +@@ -453,6 +463,7 @@ gnome_screen_cast_session_record_selections (GnomeScreenCastSession *gnome_scre + { + case SCREEN_CAST_SOURCE_TYPE_MONITOR: + if (!gnome_screen_cast_session_record_monitor (gnome_screen_cast_session, ++ info->id, + info->data.monitor, + select, + error)) +@@ -460,6 +471,7 @@ gnome_screen_cast_session_record_selections (GnomeScreenCastSession *gnome_scre + break; + case SCREEN_CAST_SOURCE_TYPE_WINDOW: + if (!gnome_screen_cast_session_record_window (gnome_screen_cast_session, ++ info->id, + info->data.window, + select, + error)) +diff --git a/src/screencast.c b/src/screencast.c +index 55a3e5c..3be8337 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -162,7 +162,7 @@ serialize_streams_as_restore_data (ScreenCastSession *screen_cast_session, + + g_variant_builder_add (&impl_builder, + "(uuv)", +- i, ++ info->id, + info->type, + stream_variant); + } +@@ -502,6 +502,7 @@ restore_stream_from_data (ScreenCastSession *screen_cast_session) + info = g_new0 (ScreenCastStreamInfo, 1); + info->type = SCREEN_CAST_SOURCE_TYPE_MONITOR; + info->data.monitor = monitor; ++ info->id = id; + g_ptr_array_add (streams, info); + } + break; +@@ -526,6 +527,7 @@ restore_stream_from_data (ScreenCastSession *screen_cast_session) + info = g_new0 (ScreenCastStreamInfo, 1); + info->type = SCREEN_CAST_SOURCE_TYPE_WINDOW; + info->data.window = window; ++ info->id = id; + g_ptr_array_add (streams, info); + } + break; +diff --git a/src/screencast.h b/src/screencast.h +index d78066e..3d64b4a 100644 +--- a/src/screencast.h ++++ b/src/screencast.h +@@ -55,6 +55,7 @@ typedef struct _ScreenCastSelection + typedef struct + { + ScreenCastSourceType type; ++ uint32_t id; + union { + Monitor *monitor; + Window *window; +diff --git a/src/screencastwidget.c b/src/screencastwidget.c +index c100ad9..20df194 100644 +--- a/src/screencastwidget.c ++++ b/src/screencastwidget.c +@@ -597,6 +597,7 @@ screen_cast_widget_get_selected_streams (ScreenCastWidget *self) + g_autoptr(GPtrArray) streams = NULL; + g_autoptr(GList) selected_monitor_rows = NULL; + g_autoptr(GList) selected_window_rows = NULL; ++ uint32_t id = 0; + GList *l; + + streams = g_ptr_array_new_with_free_func (g_free); +@@ -620,6 +621,7 @@ screen_cast_widget_get_selected_streams (ScreenCastWidget *self) + info = g_new0 (ScreenCastStreamInfo, 1); + info->type = SCREEN_CAST_SOURCE_TYPE_MONITOR; + info->data.monitor = monitor; ++ info->id = id++; + g_ptr_array_add (streams, info); + } + +@@ -634,6 +636,7 @@ screen_cast_widget_get_selected_streams (ScreenCastWidget *self) + info = g_new0 (ScreenCastStreamInfo, 1); + info->type = SCREEN_CAST_SOURCE_TYPE_WINDOW; + info->data.window = window; ++ info->id = id++; + g_ptr_array_add (streams, info); + } + diff --git a/0008-screencast-fix-variant-type-check.patch b/0008-screencast-fix-variant-type-check.patch new file mode 100644 index 0000000..79b3e4d --- /dev/null +++ b/0008-screencast-fix-variant-type-check.patch @@ -0,0 +1,24 @@ +From df8ddc91a6ab1254b22de8f092f07ce9aca4ea3c Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Thu, 25 Nov 2021 10:27:07 -0300 +Subject: [PATCH] screencast: Fix variant type check + +The restore data type is not the (suv) variant, but rather the +GNOME-specific variant format. +--- + src/screencast.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/screencast.c b/src/screencast.c +index 3be8337..c46a4a8 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -698,7 +698,7 @@ handle_select_sources (XdpImplScreenCast *object, + + if (g_variant_lookup (arg_options, "restore_data", "(suv)", &provider, &version, &restore_data)) + { +- if (!g_variant_check_format_string (restore_data, "(suv)", FALSE)) ++ if (!g_variant_check_format_string (restore_data, RESTORE_VARIANT_TYPE, FALSE)) + { + g_warning ("Cannot parse restore data, ignoring"); + goto out; diff --git a/0009-screencast-dont-restore-windows-with-too-different-titles.patch b/0009-screencast-dont-restore-windows-with-too-different-titles.patch new file mode 100644 index 0000000..1525857 --- /dev/null +++ b/0009-screencast-dont-restore-windows-with-too-different-titles.patch @@ -0,0 +1,30 @@ +From 4251d584b010c130f278ba22869d4c9a9d19ebdb Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Fri, 18 Mar 2022 13:32:11 -0300 +Subject: [PATCH] screencast: Don't restore windows with too different titles + +If the title is too different, don't restore it. The "too +different" part is arbitrarily chosen to be half of the +title's length. If this is too strict, we can review it +later. +--- + src/screencast.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/screencast.c b/src/screencast.c +index c46a4a8..5f9a738 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -455,6 +455,12 @@ find_best_window_by_app_id_and_title (const char *app_id, + } + } + ++ /* If even the best match's window title is too different, don't ++ * restore it. ++ */ ++ if (best_match_distance > strlen (title) / 2) ++ return NULL; ++ + return best_match; + } + diff --git a/0010-screencast-dont-wrap-restore-data-in-another-variant.patch b/0010-screencast-dont-wrap-restore-data-in-another-variant.patch new file mode 100644 index 0000000..90e76d3 --- /dev/null +++ b/0010-screencast-dont-wrap-restore-data-in-another-variant.patch @@ -0,0 +1,29 @@ +From 6d293e77e4470d0e5df57c29eeb6e1ed5cb619e8 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Fri, 18 Mar 2022 13:36:37 -0300 +Subject: [PATCH] screencast: Don't wrap restore data in another variant + +We were sending a variant with the wrong type. This was a +fallout from the evolution of the xdg-desktop-portal work, +which originally started as a 'v' variant, but then changed +to '(suv)', and we didn't adapt properly + +See https://github.com/flatpak/xdg-desktop-portal/pull/730 +--- + src/screencast.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/screencast.c b/src/screencast.c +index 5f9a738..6b0014a 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -229,8 +229,7 @@ on_gnome_screen_cast_session_ready (GnomeScreenCastSession *gnome_screen_cast_se + { + g_variant_builder_add (&results_builder, "{sv}", "persist_mode", + g_variant_new_uint32 (screen_cast_session->persist_mode)); +- g_variant_builder_add (&results_builder, "{sv}", "restore_data", +- g_variant_new_variant (restore_data)); ++ g_variant_builder_add (&results_builder, "{sv}", "restore_data", restore_data); + } + } + diff --git a/0011-fix-window-screencast-stream-restore.patch b/0011-fix-window-screencast-stream-restore.patch new file mode 100644 index 0000000..1dc5ace --- /dev/null +++ b/0011-fix-window-screencast-stream-restore.patch @@ -0,0 +1,390 @@ +diff --git a/src/screencast.c b/src/screencast.c +index 6b0014a..15f6a80 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -427,16 +427,20 @@ find_best_window_by_app_id_and_title (const char *app_id, + const char *title) + { + ShellIntrospect *shell_introspect = shell_introspect_get (); ++ GPtrArray *windows; + Window *best_match; + glong best_match_distance; +- GList *l; + + best_match = NULL; + best_match_distance = G_MAXLONG; + +- for (l = shell_introspect_get_windows (shell_introspect); l; l = l->next) ++ shell_introspect_ref_listeners (shell_introspect); ++ shell_introspect_wait_for_windows (shell_introspect); ++ ++ windows = shell_introspect_get_windows (shell_introspect); ++ for (size_t i = 0; windows && i < windows->len; i++) + { +- Window *window = l->data; ++ Window *window = g_ptr_array_index (windows, i); + glong distance; + + if (g_strcmp0 (window_get_app_id (window), app_id) != 0) +@@ -454,6 +458,8 @@ find_best_window_by_app_id_and_title (const char *app_id, + } + } + ++ shell_introspect_unref_listeners (shell_introspect); ++ + /* If even the best match's window title is too different, don't + * restore it. + */ +diff --git a/src/screencastwidget.c b/src/screencastwidget.c +index 9b30f2d..e95fee2 100644 +--- a/src/screencastwidget.c ++++ b/src/screencastwidget.c +@@ -164,8 +164,7 @@ update_windows_list (ScreenCastWidget *widget) + GtkListBox *window_list = GTK_LIST_BOX (widget->window_list); + GtkWidget *toplevel; + GtkWidget *child; +- GList *windows; +- GList *l; ++ GPtrArray *windows; + + child = gtk_widget_get_first_child (GTK_WIDGET (window_list)); + while (child) +@@ -181,9 +180,9 @@ update_windows_list (ScreenCastWidget *widget) + return; + + windows = shell_introspect_get_windows (widget->shell_introspect); +- for (l = windows; l; l = l->next) ++ for (size_t i = 0; windows && i < windows->len; i++) + { +- Window *window = l->data; ++ Window *window = g_ptr_array_index (windows, i); + GtkWidget *window_widget; + + if (should_skip_window (window, GTK_WINDOW (toplevel))) +diff --git a/src/shellintrospect.c b/src/shellintrospect.c +index 1fa8b93..c2b288d 100644 +--- a/src/shellintrospect.c ++++ b/src/shellintrospect.c +@@ -22,16 +22,6 @@ + #include "shell-dbus.h" + #include "shellintrospect.h" + +-enum +-{ +- WINDOWS_CHANGED, +- ANIMATIONS_ENABLED_CHANGED, +- +- N_SIGNALS +-}; +- +-static guint signals[N_SIGNALS]; +- + struct _Window + { + uint64_t id; +@@ -50,7 +40,7 @@ struct _ShellIntrospect + + unsigned int version; + +- GList *windows; ++ GPtrArray *windows; + + int num_listeners; + +@@ -60,6 +50,16 @@ struct _ShellIntrospect + + G_DEFINE_TYPE (ShellIntrospect, shell_introspect, G_TYPE_OBJECT) + ++enum ++{ ++ WINDOWS_CHANGED, ++ ANIMATIONS_ENABLED_CHANGED, ++ ++ N_SIGNALS ++}; ++ ++static guint signals[N_SIGNALS]; ++ + static ShellIntrospect *_shell_introspect; + + static void +@@ -70,50 +70,36 @@ window_free (Window *window) + g_free (window); + } + +-const char * +-window_get_title (Window *window) +-{ +- return window->title; +-} +- +-const char * +-window_get_app_id (Window *window) +-{ +- return window->app_id; +-} +- +-const uint64_t +-window_get_id (Window *window) +-{ +- return window->id; +-} +- + static void + get_windows_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) + { + ShellIntrospect *shell_introspect = user_data; ++ g_autoptr(GPtrArray) windows = NULL; + g_autoptr(GVariant) windows_variant = NULL; + g_autoptr(GError) error = NULL; + GVariantIter iter; + uint64_t id; + GVariant *params = NULL; +- GList *windows = NULL; + +- g_list_free_full (shell_introspect->windows, (GDestroyNotify) window_free); +- shell_introspect->windows = NULL; ++ g_clear_object (&shell_introspect->cancellable); + + if (!org_gnome_shell_introspect_call_get_windows_finish (shell_introspect->proxy, + &windows_variant, + res, + &error)) + { +- g_warning ("Failed to get window list: %s", error->message); ++ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) ++ g_warning ("Failed to get window list: %s", error->message); + return; + } + + g_variant_iter_init (&iter, windows_variant); ++ ++ windows = g_ptr_array_new_full (g_variant_iter_n_children (&iter), ++ (GDestroyNotify) window_free); ++ + while (g_variant_iter_loop (&iter, "{t@a{sv}}", &id, ¶ms)) + { + char *app_id = NULL; +@@ -131,64 +117,30 @@ get_windows_cb (GObject *source_object, + .title = title, + .app_id = app_id + }; +- windows = g_list_prepend (windows, window); ++ g_ptr_array_add (windows, window); + + g_clear_pointer (¶ms, g_variant_unref); + } + +- shell_introspect->windows = windows; ++ shell_introspect->windows = g_steal_pointer (&windows); + g_signal_emit (shell_introspect, signals[WINDOWS_CHANGED], 0); + } + + static void + sync_state (ShellIntrospect *shell_introspect) + { ++ g_clear_pointer (&shell_introspect->windows, g_ptr_array_unref); ++ ++ g_cancellable_cancel (shell_introspect->cancellable); ++ g_clear_object (&shell_introspect->cancellable); ++ shell_introspect->cancellable = g_cancellable_new (); ++ + org_gnome_shell_introspect_call_get_windows (shell_introspect->proxy, + shell_introspect->cancellable, + get_windows_cb, + shell_introspect); + } + +-GList * +-shell_introspect_get_windows (ShellIntrospect *shell_introspect) +-{ +- return shell_introspect->windows; +-} +- +-void +-shell_introspect_ref_listeners (ShellIntrospect *shell_introspect) +-{ +- shell_introspect->num_listeners++; +- +- if (shell_introspect->proxy) +- sync_state (shell_introspect); +-} +- +-void +-shell_introspect_unref_listeners (ShellIntrospect *shell_introspect) +-{ +- g_return_if_fail (shell_introspect->num_listeners > 0); +- +- shell_introspect->num_listeners--; +- if (shell_introspect->num_listeners == 0) +- { +- g_list_free_full (shell_introspect->windows, +- (GDestroyNotify) window_free); +- shell_introspect->windows = NULL; +- } +-} +- +-gboolean +-shell_introspect_are_animations_enabled (ShellIntrospect *shell_introspect, +- gboolean *out_animations_enabled) +-{ +- if (!shell_introspect->animations_enabled_valid) +- return FALSE; +- +- *out_animations_enabled = shell_introspect->animations_enabled; +- return TRUE; +-} +- + static void + on_windows_changed_cb (GDBusProxy *proxy, + ShellIntrospect *shell_introspect) +@@ -291,6 +243,29 @@ on_shell_introspect_name_vanished (GDBusConnection *connection, + } + } + ++static void ++shell_introspect_class_init (ShellIntrospectClass *klass) ++{ ++ signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, NULL, ++ G_TYPE_NONE, 0); ++ signals[ANIMATIONS_ENABLED_CHANGED] = ++ g_signal_new ("animations-enabled-changed", ++ G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, NULL, ++ G_TYPE_NONE, 0); ++} ++ ++static void ++shell_introspect_init (ShellIntrospect *shell_introspect) ++{ ++} ++ + ShellIntrospect * + shell_introspect_get (void) + { +@@ -311,25 +286,67 @@ shell_introspect_get (void) + return shell_introspect; + } + +-static void +-shell_introspect_init (ShellIntrospect *shell_introspect) ++GPtrArray * ++shell_introspect_get_windows (ShellIntrospect *shell_introspect) + { ++ return shell_introspect->windows; + } + +-static void +-shell_introspect_class_init (ShellIntrospectClass *klass) ++void ++shell_introspect_ref_listeners (ShellIntrospect *shell_introspect) + { +- signals[WINDOWS_CHANGED] = g_signal_new ("windows-changed", +- G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, +- 0, +- NULL, NULL, NULL, +- G_TYPE_NONE, 0); +- signals[ANIMATIONS_ENABLED_CHANGED] = +- g_signal_new ("animations-enabled-changed", +- G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST, +- 0, +- NULL, NULL, NULL, +- G_TYPE_NONE, 0); ++ shell_introspect->num_listeners++; ++ ++ if (shell_introspect->proxy) ++ sync_state (shell_introspect); ++} ++ ++void ++shell_introspect_unref_listeners (ShellIntrospect *shell_introspect) ++{ ++ g_return_if_fail (shell_introspect->num_listeners > 0); ++ ++ shell_introspect->num_listeners--; ++ if (shell_introspect->num_listeners == 0) ++ g_clear_pointer (&shell_introspect->windows, g_ptr_array_unref); ++} ++ ++const char * ++window_get_title (Window *window) ++{ ++ return window->title; ++} ++ ++const char * ++window_get_app_id (Window *window) ++{ ++ return window->app_id; ++} ++ ++const uint64_t ++window_get_id (Window *window) ++{ ++ return window->id; ++} ++ ++gboolean ++shell_introspect_are_animations_enabled (ShellIntrospect *shell_introspect, ++ gboolean *out_animations_enabled) ++{ ++ if (!shell_introspect->animations_enabled_valid) ++ return FALSE; ++ ++ *out_animations_enabled = shell_introspect->animations_enabled; ++ return TRUE; ++} ++ ++void ++shell_introspect_wait_for_windows (ShellIntrospect *shell_introspect) ++{ ++ g_assert (shell_introspect->num_listeners > 0); ++ ++ sync_state (shell_introspect); ++ ++ while (!shell_introspect->windows) ++ g_main_context_iteration (NULL, TRUE); + } +diff --git a/src/shellintrospect.h b/src/shellintrospect.h +index b187c4d..f63ecee 100644 +--- a/src/shellintrospect.h ++++ b/src/shellintrospect.h +@@ -28,19 +28,21 @@ typedef struct _Window Window; + G_DECLARE_FINAL_TYPE (ShellIntrospect, shell_introspect, + SHELL, INTROSPECT, GObject) + ++ShellIntrospect * shell_introspect_get (void); ++ ++void shell_introspect_ref_listeners (ShellIntrospect *shell_introspect); ++ ++void shell_introspect_unref_listeners (ShellIntrospect *shell_introspect); ++ + const char * window_get_app_id (Window *window); + + const char * window_get_title (Window *window); + + const uint64_t window_get_id (Window *window); + +-GList * shell_introspect_get_windows (ShellIntrospect *shell_introspect); ++GPtrArray * shell_introspect_get_windows (ShellIntrospect *shell_introspect); + +-gboolean shell_introspect_are_animations_enabled (ShellIntrospect *introspect, ++gboolean shell_introspect_are_animations_enabled (ShellIntrospect *shell_introspect, + gboolean *enable_animations); + +-void shell_introspect_ref_listeners (ShellIntrospect *shell_introspect); +- +-void shell_introspect_unref_listeners (ShellIntrospect *shell_introspect); +- +-ShellIntrospect * shell_introspect_get (void); ++void shell_introspect_wait_for_windows (ShellIntrospect *shell_introspect); diff --git a/0012-screencast-close-screencast-dialog-when-the-session-is-closed-from-client-side.patch b/0012-screencast-close-screencast-dialog-when-the-session-is-closed-from-client-side.patch new file mode 100644 index 0000000..097f453 --- /dev/null +++ b/0012-screencast-close-screencast-dialog-when-the-session-is-closed-from-client-side.patch @@ -0,0 +1,27 @@ +From 5450d65d61ea927752c001dff9f94cb9fd7baf7a Mon Sep 17 00:00:00 2001 +From: Jan Grulich +Date: Tue, 14 Jun 2022 15:39:26 +0200 +Subject: [PATCH] ScreenCast: close screencast dialog when the session is + closed from the client side + +When the session is closed, for example trying to share a screen in Chrome and closing +the web page while having the portal dialog still open, it will not take down the dialog +and instead it just gets hidden below Chrome's window. +--- + src/screencast.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/screencast.c b/src/screencast.c +index 15f6a80..729d668 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -852,6 +852,9 @@ screen_cast_session_close (Session *session) + error->message); + g_clear_object (&screen_cast_session->gnome_screen_cast_session); + } ++ ++ g_clear_pointer (&screen_cast_session->dialog_handle, ++ screen_cast_dialog_handle_close); + } + + static void diff --git a/0013-session-emit-closed-signal-when-we-are-closing-our-own-account.patch b/0013-session-emit-closed-signal-when-we-are-closing-our-own-account.patch new file mode 100644 index 0000000..b5d46a8 --- /dev/null +++ b/0013-session-emit-closed-signal-when-we-are-closing-our-own-account.patch @@ -0,0 +1,87 @@ +From b33d02e04877bfeeaa43671d3b1b48e0680fc12d Mon Sep 17 00:00:00 2001 +From: Peter Hutterer +Date: Mon, 22 Aug 2022 18:45:46 +1000 +Subject: [PATCH] session: emit the Closed signal when we're closing on our own + account + +If a RemoteDesktop/ScreenCast session is closed by mutter, send out the +Closed signal on our impl.portal.Session to notify the +xdg-desktop-portal that we're done. + +Related: https://github.com/flatpak/xdg-desktop-portal/issues/508 +--- + src/remotedesktop.c | 2 +- + src/screencast.c | 2 +- + src/session.c | 11 +++++++++-- + src/session.h | 2 +- + 4 files changed, 12 insertions(+), 5 deletions(-) + +diff --git a/src/remotedesktop.c b/src/remotedesktop.c +index 46d1dcc..0298f68 100644 +--- a/src/remotedesktop.c ++++ b/src/remotedesktop.c +@@ -266,7 +266,7 @@ static void + on_mutter_session_closed (OrgGnomeMutterRemoteDesktopSession *mutter_session_proxy, + RemoteDesktopSession *remote_desktop_session) + { +- session_close ((Session *)remote_desktop_session); ++ session_close ((Session *)remote_desktop_session, TRUE); + } + + static RemoteDesktopSession * +diff --git a/src/screencast.c b/src/screencast.c +index 15f6a80..0a15033 100644 +--- a/src/screencast.c ++++ b/src/screencast.c +@@ -243,7 +243,7 @@ static void + on_gnome_screen_cast_session_closed (GnomeScreenCastSession *gnome_screen_cast_session, + ScreenCastSession *screen_cast_session) + { +- session_close ((Session *)screen_cast_session); ++ session_close ((Session *)screen_cast_session, TRUE); + } + + static gboolean +diff --git a/src/session.c b/src/session.c +index 5ebe13f..b428ef8 100644 +--- a/src/session.c ++++ b/src/session.c +@@ -73,8 +73,15 @@ session_unexport (Session *session) + } + + void +-session_close (Session *session) ++session_close (Session *session, gboolean notify) + { ++ if (notify) ++ { ++ GVariantBuilder details_builder; ++ g_variant_builder_init (&details_builder, G_VARIANT_TYPE_VARDICT); ++ g_signal_emit_by_name (session, "closed", g_variant_builder_end (&details_builder)); ++ } ++ + if (session->exported) + session_unexport (session); + +@@ -92,7 +99,7 @@ handle_close (XdpImplSession *object, + Session *session = (Session *)object; + + if (!session->closed) +- session_close (session); ++ session_close (session, FALSE); + + xdp_impl_session_complete_close (object, invocation); + +diff --git a/src/session.h b/src/session.h +index 4a1173a..7594bd8 100644 +--- a/src/session.h ++++ b/src/session.h +@@ -47,7 +47,7 @@ Session *lookup_session (const char *id); + + Session *session_new (const char *id); + +-void session_close (Session *session); ++void session_close (Session *session, gboolean notify); + + gboolean session_export (Session *session, + GDBusConnection *connection, diff --git a/xdg-desktop-portal-gnome.spec b/xdg-desktop-portal-gnome.spec index 0b5319b..3070b82 100644 --- a/xdg-desktop-portal-gnome.spec +++ b/xdg-desktop-portal-gnome.spec @@ -5,7 +5,7 @@ Name: xdg-desktop-portal-gnome Version: 41.2 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Backend implementation for xdg-desktop-portal using GNOME License: LGPLv2+ @@ -14,6 +14,33 @@ Source0: https://download.gnome.org/sources/%{name}/41/%{name}-%{tarball_ Patch0: windows-changed-signal.patch +## Screencast: stream restoration support +# Code cleanups/shuffling +# https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/merge_requests/18 +Patch11: 0001-screencast-trivial-style-cleanups.patch +Patch12: 0002-screencast-cleanup-unnecessary-forward-declaration.patch +Patch13: 0003-screencast-more-code-shuffling-and-cleanups.patch +Patch14: 0004-screencastwidget-rework-selection-api.patch +# Stream restoration support +# https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/merge_requests/14 +Patch15: 0005-displaystatetracker-add-match-string.patch +Patch16: 0006-implement-screencast-stream-restoration.patch +Patch17: 0007-screencast-pass-stream-ids.patch +# Followup fixes +Patch18: 0008-screencast-fix-variant-type-check.patch +Patch19: 0009-screencast-dont-restore-windows-with-too-different-titles.patch +Patch20: 0010-screencast-dont-wrap-restore-data-in-another-variant.patch +# https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/merge_requests/49 +Patch21: 0011-fix-window-screencast-stream-restore.patch +# Closely related screencast fixes +# https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/merge_requests/40 +# https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/merge_requests/41 +Patch22: 0012-screencast-close-screencast-dialog-when-the-session-is-closed-from-client-side.patch +# https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/merge_requests/52 +Patch23: 0013-session-emit-closed-signal-when-we-are-closing-our-own-account.patch +# https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/merge_requests/86 +Patch24: 00014-screencasts-duplicate-monitor-and-window-stream-info.patch + BuildRequires: desktop-file-utils BuildRequires: gcc BuildRequires: gettext @@ -68,6 +95,10 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop %changelog +* Tue Jan 09 2024 Jan Grulich - 41.2-3 +- Add screencast stream restoration support + Resolves: RHEL-4526 + * Tue Dec 13 2022 Jonas Ã…dahl - 41.2-2 - Keep screen share window list up to date Resolves: #2148362