inspector: Add a button to save a recording as a node video

Take all the recorded nodes and put them into a container node, then
save that container node.

This "video" can then be played back with commands like
  gtk-rendernode-tool show --video
or similar.

Note that this actually achieves a pretty decent compression because
of the deduplication when saving rendernodes and the fact that
unchanging parts of the widget tree will reuse their nodes.
This commit is contained in:
Benjamin Otte 2024-10-07 11:38:41 +02:00
parent cc333ce5cf
commit cd9d7b7da7
2 changed files with 102 additions and 0 deletions

View File

@ -1992,6 +1992,100 @@ render_node_list_selection_changed (GtkListBox *list,
g_object_unref (paintable);
}
static GskRenderNode *
gtk_inspector_recorder_get_node_video (GtkInspectorRecorder *self)
{
GskRenderNode *result;
GPtrArray *array;
gsize i;
array = g_ptr_array_new ();
for (i = 0; i < g_list_model_get_n_items (self->recordings); i++)
{
GtkInspectorRecording *recording = g_list_model_get_item (self->recordings, i);
if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording))
g_ptr_array_add (array, gtk_inspector_render_recording_get_node (GTK_INSPECTOR_RENDER_RECORDING (recording)));
g_object_unref (recording);
}
if (array->len > 0)
result = gsk_container_node_new ((GskRenderNode **) array->pdata, array->len);
else
result = NULL;
g_ptr_array_free (array, TRUE);
return result;
}
static void
recording_save_video_response (GObject *source,
GAsyncResult *result,
gpointer node)
{
GtkFileDialog *dialog = GTK_FILE_DIALOG (source);
GFile *file;
GError *error = NULL;
file = gtk_file_dialog_save_finish (dialog, result, &error);
if (file)
{
GBytes *bytes = gsk_render_node_serialize (node);
if (!g_file_replace_contents (file,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
NULL,
FALSE,
0,
NULL,
NULL,
&error))
{
GtkAlertDialog *alert;
alert = gtk_alert_dialog_new (_("Saving RenderNode failed"));
gtk_alert_dialog_set_detail (alert, error->message);
gtk_alert_dialog_show (alert, GTK_WINDOW (gtk_window_get_transient_for (GTK_WINDOW (dialog))));
g_object_unref (alert);
g_error_free (error);
}
g_bytes_unref (bytes);
g_object_unref (file);
}
else
{
g_print ("Error saving nodes: %s\n", error->message);
g_error_free (error);
}
gsk_render_node_unref (node);
}
static void
recording_save_video (GtkButton *button,
GtkInspectorRecorder *self)
{
GtkFileDialog *dialog;
GskRenderNode *video;
video = gtk_inspector_recorder_get_node_video (self);
if (video == NULL)
return;
dialog = gtk_file_dialog_new ();
gtk_file_dialog_set_initial_name (dialog, "recording.vnode");
gtk_file_dialog_save (dialog,
GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (self))),
NULL,
recording_save_video_response, video);
g_object_unref (dialog);
}
static void
render_node_save_response (GObject *source,
GAsyncResult *result,
@ -2366,6 +2460,7 @@ gtk_inspector_recorder_class_init (GtkInspectorRecorderClass *klass)
gtk_widget_class_bind_template_callback (widget_class, recordings_clear_all);
gtk_widget_class_bind_template_callback (widget_class, recording_selected);
gtk_widget_class_bind_template_callback (widget_class, recording_save_video);
gtk_widget_class_bind_template_callback (widget_class, render_node_save);
gtk_widget_class_bind_template_callback (widget_class, render_node_clip);
//gtk_widget_class_bind_template_callback (widget_class, node_property_activated);

View File

@ -41,6 +41,13 @@
<property name="icon-name">function-linear-symbolic</property>
<property name="tooltip-text" translatable="yes">Highlight event sequences</property>
<property name="active" bind-source="GtkInspectorRecorder" bind-property="highlight-sequences" bind-flags="bidirectional|sync-create"/>
</object>
</child>
<child>
<object class="GtkButton">
<property name="icon-name">document-save-as-symbolic</property>
<property name="tooltip-text" translatable="yes">Save nodes as video</property>
<signal name="clicked" handler="recording_save_video"/>
<property name="halign">start</property>
<property name="hexpand">1</property>
</object>