commit 55d0af93d5c78c9e4ac8e3566a166694f86ecad7
parent cf7bcd8921223e8ff6db047b36da7412408fedea
Author: Jacki <jacki@thejackimonster.de>
Date: Sat, 30 Mar 2024 19:28:35 +0100
Fix memory leak of messages in open chats and reduce footprint of file previews
Signed-off-by: Jacki <jacki@thejackimonster.de>
Diffstat:
7 files changed, 314 insertions(+), 204 deletions(-)
diff --git a/src/file.c b/src/file.c
@@ -23,6 +23,7 @@
*/
#include "file.h"
+#include <gnunet/gnunet_chat_lib.h>
void
file_create_info(struct GNUNET_CHAT_File *file)
@@ -37,6 +38,13 @@ file_create_info(struct GNUNET_CHAT_File *file)
info->update_task = 0;
info->file_messages = NULL;
+ info->preview_image = NULL;
+ info->preview_animation = NULL;
+ info->preview_animation_iter = NULL;
+
+ info->redraw_animation_task = 0;
+ info->preview_widgets = NULL;
+
GNUNET_CHAT_file_set_user_pointer(file, info);
}
@@ -50,6 +58,8 @@ file_destroy_info(struct GNUNET_CHAT_File *file)
if (!info)
return;
+ file_unload_preview_image(file);
+
if (info->update_task)
g_source_remove(info->update_task);
@@ -76,6 +86,42 @@ file_add_ui_message_to_info(const struct GNUNET_CHAT_File *file,
}
void
+file_add_widget_to_preview(const struct GNUNET_CHAT_File *file,
+ GtkWidget *widget)
+{
+ g_assert(widget);
+
+ MESSENGER_FileInfo* info = GNUNET_CHAT_file_get_user_pointer(file);
+
+ if (!info)
+ return;
+
+ info->preview_widgets = g_list_append(info->preview_widgets, widget);
+
+ if ((info->preview_image) ||
+ (info->preview_animation) ||
+ (info->preview_animation_iter))
+ gtk_widget_queue_draw(widget);
+}
+
+void
+file_remove_widget_from_preview(const struct GNUNET_CHAT_File *file,
+ GtkWidget *widget)
+{
+ g_assert(widget);
+
+ MESSENGER_FileInfo* info = GNUNET_CHAT_file_get_user_pointer(file);
+
+ if (!info)
+ return;
+
+ info->preview_widgets = g_list_remove(info->preview_widgets, widget);
+
+ if (!(info->preview_widgets))
+ file_unload_preview_image(file);
+}
+
+void
file_update_upload_info(const struct GNUNET_CHAT_File *file,
uint64_t completed,
uint64_t size)
@@ -154,3 +200,131 @@ file_update_download_info(const struct GNUNET_CHAT_File *file,
info->app = app;
info->update_task = g_idle_add(file_update_messages, info);
}
+
+static void
+file_draw_preview(MESSENGER_FileInfo* info)
+{
+ g_assert(info);
+
+ GList *list = info->preview_widgets;
+
+ while (list)
+ {
+ GtkWidget *widget = GTK_WIDGET(list->data);
+
+ gtk_widget_queue_draw(widget);
+
+ list = list->next;
+ }
+}
+
+void
+file_load_preview_image(struct GNUNET_CHAT_File *file)
+{
+ MESSENGER_FileInfo* info = GNUNET_CHAT_file_get_user_pointer(file);
+
+ if (!info)
+ return;
+
+ const char *preview = GNUNET_CHAT_file_open_preview(file);
+
+ if (!preview)
+ return;
+
+ file_unload_preview_image(file);
+
+ info->preview_animation = gdk_pixbuf_animation_new_from_file(
+ preview, NULL
+ );
+
+ if (!(info->preview_animation))
+ info->preview_image = gdk_pixbuf_new_from_file(preview, NULL);
+
+ GNUNET_CHAT_file_close_preview(file);
+
+ if (info->preview_widgets)
+ file_draw_preview(info);
+}
+
+void
+file_unload_preview_image(const struct GNUNET_CHAT_File *file)
+{
+ MESSENGER_FileInfo* info = GNUNET_CHAT_file_get_user_pointer(file);
+
+ if (!info)
+ return;
+
+ if (info->preview_image)
+ {
+ g_object_unref(info->preview_image);
+ info->preview_image = NULL;
+ }
+
+ if (info->redraw_animation_task)
+ {
+ g_source_remove(info->redraw_animation_task);
+ info->redraw_animation_task = 0;
+ }
+
+ if (info->preview_animation_iter)
+ {
+ g_object_unref(info->preview_animation_iter);
+ info->preview_animation_iter = NULL;
+ }
+
+ if (info->preview_animation)
+ {
+ g_object_unref(info->preview_animation);
+ info->preview_animation = NULL;
+ }
+}
+
+static gboolean
+file_redraw_animation(gpointer user_data)
+{
+ g_assert(user_data);
+
+ MESSENGER_FileInfo* info = (MESSENGER_FileInfo*) user_data;
+
+ info->redraw_animation_task = 0;
+
+ file_draw_preview(info);
+
+ return FALSE;
+}
+
+GdkPixbuf*
+file_get_current_preview_image(const struct GNUNET_CHAT_File *file)
+{
+ MESSENGER_FileInfo* info = GNUNET_CHAT_file_get_user_pointer(file);
+
+ if (!info)
+ return NULL;
+
+ GdkPixbuf *image = info->preview_image;
+
+ if (!(info->preview_animation))
+ return image;
+
+ if (info->preview_animation_iter)
+ gdk_pixbuf_animation_iter_advance(info->preview_animation_iter, NULL);
+ else
+ info->preview_animation_iter = gdk_pixbuf_animation_get_iter(
+ info->preview_animation, NULL
+ );
+
+ image = gdk_pixbuf_animation_iter_get_pixbuf(info->preview_animation_iter);
+
+ if (!(info->redraw_animation_task))
+ {
+ const int delay = gdk_pixbuf_animation_iter_get_delay_time(
+ info->preview_animation_iter
+ );
+
+ info->redraw_animation_task = g_timeout_add(
+ delay, file_redraw_animation, info
+ );
+ }
+
+ return image;
+}
diff --git a/src/file.h b/src/file.h
@@ -34,6 +34,13 @@ typedef struct MESSENGER_FileInfo
guint update_task;
GList *file_messages;
+
+ GdkPixbuf *preview_image;
+ GdkPixbufAnimation *preview_animation;
+ GdkPixbufAnimationIter *preview_animation_iter;
+
+ guint redraw_animation_task;
+ GList *preview_widgets;
} MESSENGER_FileInfo;
/**
@@ -66,6 +73,29 @@ file_add_ui_message_to_info(const struct GNUNET_CHAT_File *file,
UI_MESSAGE_Handle *message);
/**
+ * Adds a widget to the list of widgets which get
+ * redrawn automatically when displaying an animation.
+ *
+ * @param file Chat file
+ * @param widget Preview widget
+ */
+void
+file_add_widget_to_preview(const struct GNUNET_CHAT_File *file,
+ GtkWidget *widget);
+
+/**
+ * Removes a widgets from the list of widgets which
+ * get redrawn automatically when displaying an
+ * animation.
+ *
+ * @param file Chat file
+ * @param widget Preview widget
+ */
+void
+file_remove_widget_from_preview(const struct GNUNET_CHAT_File *file,
+ GtkWidget *widget);
+
+/**
* Updates the connected UI elements for a given
* file depending on the current state of its upload
* process.
@@ -95,4 +125,31 @@ file_update_download_info(const struct GNUNET_CHAT_File *file,
uint64_t completed,
uint64_t size);
+/**
+ * Loads required image data for a given file into memory
+ * to display a preview image.
+ *
+ * @param file Chat file
+ */
+void
+file_load_preview_image(struct GNUNET_CHAT_File *file);
+
+/**
+ * Unloads/Frees required image data of a given file from
+ * memory to displaying a preview image.
+ *
+ * @param file Chat file
+ */
+void
+file_unload_preview_image(const struct GNUNET_CHAT_File *file);
+
+/**
+ * Returns the current image data to preview a given file
+ * as animated or static image.
+ *
+ * @param file Chat file
+ */
+GdkPixbuf*
+file_get_current_preview_image(const struct GNUNET_CHAT_File *file);
+
#endif /* FILE_H_ */
diff --git a/src/ui/chat.c b/src/ui/chat.c
@@ -2020,13 +2020,15 @@ iterate_ui_chat_update_context_media(void *cls,
UI_MEDIA_PREVIEW_Handle* handle = ui_media_preview_new(closure->app);
ui_media_preview_update(handle, file);
- if ((! handle->preview_animation) && (! handle->preview_image))
+ GdkPixbuf *image = file_get_current_preview_image(file);
+
+ if (!image)
{
ui_media_preview_delete(handle);
return GNUNET_YES;
}
- gtk_flow_box_insert(flowbox, handle->media_box, -1);
+ gtk_flow_box_insert(flowbox, handle->media_box, 0);
GtkFlowBoxChild *child = GTK_FLOW_BOX_CHILD(
gtk_widget_get_parent(handle->media_box)
@@ -2223,6 +2225,30 @@ ui_chat_delete(UI_CHAT_Handle *handle)
{
g_assert(handle);
+ GList *message_rows = gtk_container_get_children(GTK_CONTAINER(handle->messages_listbox));
+ GList *row_element = message_rows;
+
+ while (row_element)
+ {
+ GtkWidget *row = GTK_WIDGET(row_element->data);
+
+ if (!row)
+ goto skip_row;
+
+ UI_MESSAGE_Handle *message = (UI_MESSAGE_Handle*) g_object_get_qdata(
+ G_OBJECT(row), handle->app->quarks.ui
+ );
+
+ if (message)
+ ui_chat_remove_message(handle, handle->app, message);
+
+ skip_row:
+ row_element = row_element->next;
+ }
+
+ if (message_rows)
+ g_list_free(message_rows);
+
ui_picker_delete(handle->picker);
g_object_unref(handle->builder);
diff --git a/src/ui/media_preview.c b/src/ui/media_preview.c
@@ -25,24 +25,7 @@
#include "media_preview.h"
#include "../application.h"
-
-static int
-handle_media_preview_redraw_animation(gpointer user_data)
-{
- g_assert(user_data);
-
- UI_MEDIA_PREVIEW_Handle *handle = (UI_MEDIA_PREVIEW_Handle*) user_data;
-
- handle->redraw_animation = 0;
-
- if ((handle->preview_drawing_area) &&
- ((handle->preview_image) ||
- (handle->preview_animation) ||
- (handle->preview_animation_iter)))
- gtk_widget_queue_draw(GTK_WIDGET(handle->preview_drawing_area));
-
- return FALSE;
-}
+#include "../file.h"
static gboolean
handle_preview_drawing_area_draw(GtkWidget* drawing_area,
@@ -60,29 +43,16 @@ handle_preview_drawing_area_draw(GtkWidget* drawing_area,
gtk_render_background(context, cairo, 0, 0, width, height);
- GdkPixbuf *image = handle->preview_image;
-
- if (!(handle->preview_animation))
- goto render_image;
-
- if (handle->preview_animation_iter)
- gdk_pixbuf_animation_iter_advance(handle->preview_animation_iter, NULL);
- else
- handle->preview_animation_iter = gdk_pixbuf_animation_get_iter(
- handle->preview_animation, NULL
- );
-
- image = gdk_pixbuf_animation_iter_get_pixbuf(handle->preview_animation_iter);
-
- const int delay = gdk_pixbuf_animation_iter_get_delay_time(
- handle->preview_animation_iter
+ struct GNUNET_CHAT_File *file = (struct GNUNET_CHAT_File *) g_object_get_qdata(
+ G_OBJECT(handle->preview_drawing_area),
+ handle->app->quarks.data
);
- handle->redraw_animation = g_timeout_add(
- delay, handle_media_preview_redraw_animation, handle
- );
+ if (!file)
+ return FALSE;
+
+ GdkPixbuf *image = file_get_current_preview_image(file);
-render_image:
if (!image)
return FALSE;
@@ -136,45 +106,9 @@ render_image:
cairo_fill(cairo);
g_object_unref(scaled);
- if (handle->preview_image)
- {
- g_object_unref(handle->preview_image);
- handle->preview_image = NULL;
- }
-
return FALSE;
}
-static void
-_clear_message_preview_data(UI_MEDIA_PREVIEW_Handle *handle)
-{
- g_assert(handle);
-
- if (handle->preview_image)
- {
- g_object_unref(handle->preview_image);
- handle->preview_image = NULL;
- }
-
- if (handle->redraw_animation)
- {
- g_source_remove(handle->redraw_animation);
- handle->redraw_animation = 0;
- }
-
- if (handle->preview_animation_iter)
- {
- g_object_unref(handle->preview_animation_iter);
- handle->preview_animation_iter = NULL;
- }
-
- if (handle->preview_animation)
- {
- g_object_unref(handle->preview_animation);
- handle->preview_animation = NULL;
- }
-}
-
UI_MEDIA_PREVIEW_Handle*
ui_media_preview_new(MESSENGER_Application *app)
{
@@ -194,6 +128,8 @@ ui_media_preview_new(MESSENGER_Application *app)
gtk_builder_get_object(handle->builder, "preview_drawing_area")
);
+ handle->app = app;
+
g_signal_connect(
handle->preview_drawing_area,
"draw",
@@ -201,12 +137,6 @@ ui_media_preview_new(MESSENGER_Application *app)
handle
);
- handle->preview_image = NULL;
- handle->preview_animation = NULL;
- handle->preview_animation_iter = NULL;
-
- handle->redraw_animation = 0;
-
return handle;
}
@@ -216,22 +146,22 @@ ui_media_preview_update(UI_MEDIA_PREVIEW_Handle *handle,
{
g_assert((handle) && (file));
- const char *preview = GNUNET_CHAT_file_open_preview(file);
-
- if (!preview)
- return;
-
- handle->preview_animation = gdk_pixbuf_animation_new_from_file(
- preview, NULL
+ struct GNUNET_CHAT_File *previous = (struct GNUNET_CHAT_File *) g_object_get_qdata(
+ G_OBJECT(handle->preview_drawing_area),
+ handle->app->quarks.data
);
- if (!(handle->preview_animation))
- handle->preview_image = gdk_pixbuf_new_from_file(preview, NULL);
+ if (previous)
+ file_remove_widget_from_preview(previous, GTK_WIDGET(handle->preview_drawing_area));
- GNUNET_CHAT_file_close_preview(file);
+ file_load_preview_image(file);
+ file_add_widget_to_preview(file, GTK_WIDGET(handle->preview_drawing_area));
- if ((handle->preview_animation) || (handle->preview_image))
- gtk_widget_queue_draw(GTK_WIDGET(handle->preview_drawing_area));
+ g_object_set_qdata(
+ G_OBJECT(handle->preview_drawing_area),
+ handle->app->quarks.data,
+ file
+ );
}
void
@@ -239,7 +169,13 @@ ui_media_preview_delete(UI_MEDIA_PREVIEW_Handle *handle)
{
g_assert(handle);
- _clear_message_preview_data(handle);
+ struct GNUNET_CHAT_File *file = (struct GNUNET_CHAT_File *) g_object_get_qdata(
+ G_OBJECT(handle->preview_drawing_area),
+ handle->app->quarks.data
+ );
+
+ if (file)
+ file_remove_widget_from_preview(file, GTK_WIDGET(handle->preview_drawing_area));
g_object_unref(handle->builder);
diff --git a/src/ui/media_preview.h b/src/ui/media_preview.h
@@ -34,14 +34,10 @@ typedef struct UI_MEDIA_PREVIEW_Handle
GtkBuilder *builder;
GtkWidget *media_box;
-
+
GtkDrawingArea *preview_drawing_area;
- GdkPixbuf *preview_image;
- GdkPixbufAnimation *preview_animation;
- GdkPixbufAnimationIter *preview_animation_iter;
-
- guint redraw_animation;
+ MESSENGER_Application *app;
} UI_MEDIA_PREVIEW_Handle;
/**
diff --git a/src/ui/message.c b/src/ui/message.c
@@ -158,24 +158,6 @@ handle_media_button_click(GtkButton *button,
g_string_free(uri, TRUE);
}
-static int
-handle_message_redraw_animation(gpointer user_data)
-{
- g_assert(user_data);
-
- UI_MESSAGE_Handle *handle = (UI_MESSAGE_Handle*) user_data;
-
- handle->redraw_animation = 0;
-
- if ((handle->preview_drawing_area) &&
- ((handle->preview_image) ||
- (handle->preview_animation) ||
- (handle->preview_animation_iter)))
- gtk_widget_queue_draw(GTK_WIDGET(handle->preview_drawing_area));
-
- return FALSE;
-}
-
static gboolean
handle_preview_drawing_area_draw(GtkWidget* drawing_area,
cairo_t* cairo,
@@ -192,29 +174,16 @@ handle_preview_drawing_area_draw(GtkWidget* drawing_area,
gtk_render_background(context, cairo, 0, 0, width, height);
- GdkPixbuf *image = handle->preview_image;
-
- if (!(handle->preview_animation))
- goto render_image;
-
- if (handle->preview_animation_iter)
- gdk_pixbuf_animation_iter_advance(handle->preview_animation_iter, NULL);
- else
- handle->preview_animation_iter = gdk_pixbuf_animation_get_iter(
- handle->preview_animation, NULL
- );
-
- image = gdk_pixbuf_animation_iter_get_pixbuf(handle->preview_animation_iter);
-
- const int delay = gdk_pixbuf_animation_iter_get_delay_time(
- handle->preview_animation_iter
+ struct GNUNET_CHAT_File *file = (struct GNUNET_CHAT_File *) g_object_get_qdata(
+ G_OBJECT(handle->message_box),
+ handle->app->quarks.data
);
- handle->redraw_animation = g_timeout_add(
- delay, handle_message_redraw_animation, handle
- );
+ if (!file)
+ return FALSE;
+
+ GdkPixbuf *image = file_get_current_preview_image(file);
-render_image:
if (!image)
return FALSE;
@@ -260,43 +229,6 @@ render_image:
return FALSE;
}
-static void
-_clear_message_preview_data(UI_MESSAGE_Handle *handle)
-{
- g_assert(handle);
-
- if (handle->preview_image)
- {
- g_object_unref(handle->preview_image);
- handle->preview_image = NULL;
- }
-
- if (handle->redraw_animation)
- {
- g_source_remove(handle->redraw_animation);
- handle->redraw_animation = 0;
- }
-
- if (handle->preview_animation_iter)
- {
- g_object_unref(handle->preview_animation_iter);
- handle->preview_animation_iter = NULL;
- }
-
- if (handle->preview_animation)
- {
- g_object_unref(handle->preview_animation);
- handle->preview_animation = NULL;
- }
-
- if (handle->preview_drawing_area)
- gtk_widget_set_size_request(
- GTK_WIDGET(handle->preview_drawing_area),
- -1,
- -1
- );
-}
-
UI_MESSAGE_Handle*
ui_message_new(MESSENGER_Application *app,
UI_MESSAGE_Type type)
@@ -461,6 +393,8 @@ ui_message_new(MESSENGER_Application *app,
gtk_builder_get_object(handle->builder[1], "media_button")
);
+ handle->app = app;
+
g_signal_connect(
handle->media_button,
"clicked",
@@ -483,12 +417,6 @@ ui_message_new(MESSENGER_Application *app,
gtk_builder_get_object(handle->builder[1], "message_content_box")
));
- handle->preview_image = NULL;
- handle->preview_animation = NULL;
- handle->preview_animation_iter = NULL;
-
- handle->redraw_animation = 0;
-
return handle;
}
@@ -613,30 +541,21 @@ _update_file_message(UI_MESSAGE_Handle *handle,
);
if ((app->settings.accept_all_files) &&
- (!GNUNET_CHAT_file_is_downloading(file)))
+ (GNUNET_YES != GNUNET_CHAT_file_is_downloading(file)))
autostart_download = TRUE;
goto file_content;
}
- if (!(handle->preview_drawing_area))
+ if ((!(handle->preview_drawing_area)) ||
+ (GNUNET_CHAT_file_get_size(file) != GNUNET_CHAT_file_get_local_size(file)))
goto file_progress;
- const char *preview = GNUNET_CHAT_file_open_preview(file);
+ file_load_preview_image(file);
- if (!preview)
- goto file_progress;
+ GdkPixbuf *image = file_get_current_preview_image(file);
- handle->preview_animation = gdk_pixbuf_animation_new_from_file(
- preview, NULL
- );
-
- if (!(handle->preview_animation))
- handle->preview_image = gdk_pixbuf_new_from_file(preview, NULL);
-
- GNUNET_CHAT_file_close_preview(file);
-
- if ((handle->preview_animation) || (handle->preview_image))
+ if (image)
{
gtk_widget_set_size_request(
GTK_WIDGET(handle->preview_drawing_area),
@@ -649,7 +568,7 @@ _update_file_message(UI_MESSAGE_Handle *handle,
GTK_WIDGET(handle->preview_drawing_area)
);
- gtk_widget_queue_draw(GTK_WIDGET(handle->preview_drawing_area));
+ file_add_widget_to_preview(file, GTK_WIDGET(handle->preview_drawing_area));
return;
}
@@ -914,7 +833,13 @@ ui_message_delete(UI_MESSAGE_Handle *handle,
if (children)
g_list_free(children);
- _clear_message_preview_data(handle);
+ struct GNUNET_CHAT_File *file = (struct GNUNET_CHAT_File *) g_object_get_qdata(
+ G_OBJECT(handle->message_box),
+ app->quarks.data
+ );
+
+ if (file)
+ file_remove_widget_from_preview(file, GTK_WIDGET(handle->preview_drawing_area));
g_object_unref(handle->builder[1]);
g_object_unref(handle->builder[0]);
diff --git a/src/ui/message.h b/src/ui/message.h
@@ -84,11 +84,7 @@ typedef struct UI_MESSAGE_Handle
GtkProgressBar *media_progress_bar;
GtkButton *media_button;
- GdkPixbuf *preview_image;
- GdkPixbufAnimation *preview_animation;
- GdkPixbufAnimationIter *preview_animation_iter;
-
- guint redraw_animation;
+ MESSENGER_Application *app;
} UI_MESSAGE_Handle;
/**