messenger-gtk

Gtk+3 graphical user interfaces for GNUnet Messenger
Log | Files | Refs | Submodules | README | LICENSE

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:
Msrc/file.c | 174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/file.h | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/ui/chat.c | 30++++++++++++++++++++++++++++--
Msrc/ui/media_preview.c | 122+++++++++++++++++++------------------------------------------------------------
Msrc/ui/media_preview.h | 8++------
Msrc/ui/message.c | 121+++++++++++++++----------------------------------------------------------------
Msrc/ui/message.h | 6+-----
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; /**