Thu, 02 Mar 2023 23:58:21 -0600
Fix leaks in debug window
Fixes two leaks from the debug window:
```
22 bytes in 1 blocks are definitely lost in loss record 7,038 of 41,972
at 0x484386F: malloc (vg_replace_malloc.c:393)
by 0x4980168: g_malloc (gmem.c:130)
by 0x4995602: g_strdup (gstrfuncs.c:363)
by 0x4CFB782: purple_prefs_connect_callback (prefs.c:1301)
by 0x48AA524: pidgin_debug_init (pidgindebug.c:918)
by 0x48B8F9E: pidgin_ui_start (pidginui.c:146)
by 0x4D37B01: purple_ui_start (purpleui.c:397)
by 0x4CE3BA6: purple_core_init (core.c:211)
by 0x48A384F: pidgin_application_startup (pidginapplication.c:820)
by 0x4DD3553: UnknownInlinedFun (gclosure.c:895)
by 0x4DD3553: g_signal_emit_valist (gsignal.c:3456)
by 0x4DD3632: g_signal_emit (gsignal.c:3606)
by 0x5815B82: g_application_register (gapplication.c:2211)
```
and
```
32 bytes in 1 blocks are definitely lost in loss record 15,308 of 41,972
at 0x484386F: malloc (vg_replace_malloc.c:393)
by 0x4980168: g_malloc (gmem.c:130)
by 0x4997AB5: g_slice_alloc (gslice.c:1074)
by 0x495E14C: UnknownInlinedFun (gdatetime.c:678)
by 0x495E14C: g_date_time_from_instant (gdatetime.c:774)
by 0x495F4E9: g_date_time_new_now_local (gdatetime.c:989)
by 0x48A82F9: save_response_cb (pidgindebug.c:136)
by 0x4DB4375: g_cclosure_marshal_VOID__INTv (gmarshal.c:596)
by 0x4DD3553: UnknownInlinedFun (gclosure.c:895)
by 0x4DD3553: g_signal_emit_valist (gsignal.c:3456)
by 0x4DD3632: g_signal_emit (gsignal.c:3606)
by 0x4DB5FBF: g_closure_invoke (gclosure.c:832)
by 0x4DE3D85: signal_emit_unlocked_R.isra.0 (gsignal.c:3796)
by 0x4DD3419: g_signal_emit_valist (gsignal.c:3549)
```
Testing Done:
Ran in valgrind, saved the debug logs to a file, and the above leaks were gone.
Reviewed at https://reviews.imfreedom.org/r/2304/
/* pidgin * * Pidgin is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include <glib/gi18n-lib.h> #include <gtk/gtk.h> #include <purple.h> #include "gtksavedstatuses.h" #include "pidginiconname.h" enum { SS_MENU_ENTRY_TYPE_PRIMITIVE, SS_MENU_ENTRY_TYPE_SAVEDSTATUS }; enum { /* _SSMenuEntryType */ SS_MENU_TYPE_COLUMN, /* * This is a GdkPixbuf (the other columns are strings). * This column is visible. */ SS_MENU_ICON_COLUMN, /* The text displayed on the status box. This column is visible. */ SS_MENU_TEXT_COLUMN, /* * This value depends on SS_MENU_TYPE_COLUMN. For _SAVEDSTATUS types, * this is the creation time. For _PRIMITIVE types, * this is the PurpleStatusPrimitive. */ SS_MENU_DATA_COLUMN, /* * This is the emblem to use for this status */ SS_MENU_EMBLEM_COLUMN, /* * And whether or not that emblem is visible */ SS_MENU_EMBLEM_VISIBLE_COLUMN, SS_MENU_NUM_COLUMNS }; static void status_menu_cb(GtkComboBox *widget, void(*callback)(PurpleSavedStatus*)) { GtkTreeIter iter; int type; gpointer data; PurpleSavedStatus *status = NULL; if (!gtk_combo_box_get_active_iter(widget, &iter)) return; gtk_tree_model_get(gtk_combo_box_get_model(widget), &iter, SS_MENU_TYPE_COLUMN, &type, SS_MENU_DATA_COLUMN, &data, -1); if (type == SS_MENU_ENTRY_TYPE_PRIMITIVE) { PurpleStatusPrimitive primitive = GPOINTER_TO_INT(data); status = purple_savedstatus_find_transient_by_type_and_message(primitive, NULL); if (status == NULL) status = purple_savedstatus_new(NULL, primitive); } else if (type == SS_MENU_ENTRY_TYPE_SAVEDSTATUS) status = purple_savedstatus_find_by_creation_time(GPOINTER_TO_INT(data)); callback(status); } static gint saved_status_sort_alphabetically_func(gconstpointer a, gconstpointer b) { const PurpleSavedStatus *saved_status_a = a; const PurpleSavedStatus *saved_status_b = b; return g_utf8_collate(purple_savedstatus_get_title(saved_status_a), purple_savedstatus_get_title(saved_status_b)); } static gboolean pidgin_status_menu_add_primitive(GtkListStore *model, G_GNUC_UNUSED GtkWidget *w, PurpleStatusPrimitive primitive, PurpleSavedStatus *current_status) { GtkTreeIter iter; gboolean currently_selected = FALSE; gtk_list_store_append(model, &iter); gtk_list_store_set(model, &iter, SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_PRIMITIVE, SS_MENU_ICON_COLUMN, pidgin_icon_name_from_status_primitive(primitive, NULL), SS_MENU_TEXT_COLUMN, purple_primitive_get_name_from_type(primitive), SS_MENU_DATA_COLUMN, GINT_TO_POINTER(primitive), SS_MENU_EMBLEM_VISIBLE_COLUMN, FALSE, -1); if (purple_savedstatus_is_transient(current_status) && !purple_savedstatus_has_substatuses(current_status) && purple_savedstatus_get_primitive_type(current_status) == primitive) currently_selected = TRUE; return currently_selected; } static void pidgin_status_menu_update_iter(GtkWidget *combobox, GtkListStore *store, GtkTreeIter *iter, PurpleSavedStatus *status) { PurpleStatusPrimitive primitive; if (store == NULL) store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox))); primitive = purple_savedstatus_get_primitive_type(status); gtk_list_store_set(store, iter, SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS, SS_MENU_ICON_COLUMN, pidgin_icon_name_from_status_primitive(primitive, NULL), SS_MENU_TEXT_COLUMN, purple_savedstatus_get_title(status), SS_MENU_DATA_COLUMN, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status)), SS_MENU_EMBLEM_COLUMN, "document-save", SS_MENU_EMBLEM_VISIBLE_COLUMN, TRUE, -1); } static gboolean pidgin_status_menu_find_iter(GtkListStore *store, GtkTreeIter *iter, PurpleSavedStatus *find) { int type; gpointer data; time_t creation_time = purple_savedstatus_get_creation_time(find); GtkTreeModel *model = GTK_TREE_MODEL(store); if (!gtk_tree_model_get_iter_first(model, iter)) return FALSE; do { gtk_tree_model_get(model, iter, SS_MENU_TYPE_COLUMN, &type, SS_MENU_DATA_COLUMN, &data, -1); if (type == SS_MENU_ENTRY_TYPE_PRIMITIVE) continue; if (GPOINTER_TO_INT(data) == creation_time) return TRUE; } while (gtk_tree_model_iter_next(model, iter)); return FALSE; } static void savedstatus_added_cb(PurpleSavedStatus *status, GtkWidget *combobox) { GtkListStore *store; GtkTreeIter iter; if (purple_savedstatus_is_transient(status)) return; store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox))); gtk_list_store_append(store, &iter); pidgin_status_menu_update_iter(combobox, store, &iter, status); } static void savedstatus_deleted_cb(PurpleSavedStatus *status, GtkWidget *combobox) { GtkListStore *store; GtkTreeIter iter; if (purple_savedstatus_is_transient(status)) return; store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox))); if (pidgin_status_menu_find_iter(store, &iter, status)) gtk_list_store_remove(store, &iter); } static void savedstatus_modified_cb(PurpleSavedStatus *status, GtkWidget *combobox) { GtkListStore *store; GtkTreeIter iter; if (purple_savedstatus_is_transient(status)) return; store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox))); if (pidgin_status_menu_find_iter(store, &iter, status)) pidgin_status_menu_update_iter(combobox, store, &iter, status); } GtkWidget *pidgin_status_menu(PurpleSavedStatus *current_status, GCallback callback) { GtkWidget *combobox; GtkListStore *model; GList *sorted, *cur; int i = 0; int index = -1; GtkTreeIter iter; GtkCellRenderer *text_rend; GtkCellRenderer *icon_rend; GtkCellRenderer *emblem_rend; model = gtk_list_store_new(SS_MENU_NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN); combobox = gtk_combo_box_new(); if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_AVAILABLE, current_status)) index = i; i++; if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_AWAY, current_status)) index = i; i++; if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_INVISIBLE, current_status)) index = i; i++; if (pidgin_status_menu_add_primitive(model, combobox, PURPLE_STATUS_OFFLINE, current_status)) index = i; i++; sorted = g_list_copy((GList *)purple_savedstatuses_get_all()); sorted = g_list_sort(sorted, saved_status_sort_alphabetically_func); for (cur = sorted; cur; cur = cur->next) { PurpleSavedStatus *status = (PurpleSavedStatus *) cur->data; if (!purple_savedstatus_is_transient(status)) { gtk_list_store_append(model, &iter); pidgin_status_menu_update_iter(combobox, model, &iter, status); if (status == current_status) index = i; i++; } } g_list_free(sorted); gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(model)); text_rend = gtk_cell_renderer_text_new(); icon_rend = gtk_cell_renderer_pixbuf_new(); emblem_rend = gtk_cell_renderer_pixbuf_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), icon_rend, FALSE); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), text_rend, TRUE); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), emblem_rend, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), icon_rend, "icon-name", SS_MENU_ICON_COLUMN, NULL); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), text_rend, "markup", SS_MENU_TEXT_COLUMN, NULL); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combobox), emblem_rend, "icon-name", SS_MENU_EMBLEM_COLUMN, "visible", SS_MENU_EMBLEM_VISIBLE_COLUMN, NULL); g_object_set(text_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), index); g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(status_menu_cb), callback); /* Make sure the list is updated dynamically when a substatus is changed/deleted * or a new one is added. */ purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-added", combobox, G_CALLBACK(savedstatus_added_cb), combobox); purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-deleted", combobox, G_CALLBACK(savedstatus_deleted_cb), combobox); purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-modified", combobox, G_CALLBACK(savedstatus_modified_cb), combobox); g_signal_connect(G_OBJECT(combobox), "destroy", G_CALLBACK(purple_signals_disconnect_by_handle), NULL); return combobox; }