Unexpected abort using GtkTextBuffer - c

I am developing a Gtk based application where I need to update every X seconds a GtkTextBuffer, by concatenating the text that is already there. Apparently it succeeds but sometimes "randomly" the application aborts and I assume that it is due to this element, as it wasn't happening before. It sometimes aborts when there is a second/third update (each update only adds one new line) and sometimes it does not abort at all. I have based the TextBuffer update function on simple chat applications behavior but I don't know what I am doing wrong. The update function is:
void updateTextWindow(char *new_msg){
g_print("Msg rcv: %s\n", new_msg);
GtkTextIter start, end;
GtkTextBuffer *buffer = gtk_text_view_get_buffer (global_text_window);
//global_text_window is a global gtkwidget where TextBuffer is embedded
gchar *previous_text;
gtk_text_buffer_get_bounds (buffer, &start, &end);
previous_text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
strcat(previous_text, "\n");
strcat(previous_text, new_msg);
gtk_text_buffer_set_text(buffer, previous_text, -1);
g_free(previous_text);
}
Moreover the related code in the main function where all GtkWidgets are declared and initializated is:
void create_ui (CustomData *data) {
[...]
GtkWidget *secondary_box; /* for text entry & buffer */
GtkScrolledWindow *scrolling_box;
GtkWidget *text_window;
GtkTextBuffer *buffer;
GtkWidget *text_entry;
secondary_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
gtk_widget_set_size_request(secondary_box, 600, -1);
scrolling_box = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(scrolling_box, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
text_entry = gtk_entry_new();
global_text_entry = text_entry;
gtk_entry_set_max_length(text_entry, 25);
text_window = gtk_text_view_new();
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_window));
gtk_text_buffer_set_text(buffer, "\n", -1);
gtk_text_view_set_editable(text_window, FALSE);
global_text_window = text_window;
gtk_scrolled_window_add_with_viewport(scrolling_box, text_window);
gtk_box_pack_end(GTK_CONTAINER(secondary_box), text_entry, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(text_entry), "activate", G_CALLBACK(text_entry_cb), &data);
gtk_box_pack_end(GTK_CONTAINER(secondary_box), scrolling_box, TRUE, TRUE, 0);
[...]
}
The update function is called from another thread, not from the "activate" callback for the text entry widget.
Thanks for your help

That code doesn't make sense. You're concatenating into a string that doesn't have space to hold more text, that's undefined behavior waiting to happen! Your posted answer doesn't make sense either, there's no point in creating a copy of the text like that. That might help just because it uses the heap a bit more, so perhaps you get luckier. Still undefined behavior to concatenate into previous_text.
If you want to append more text, there's no need to re-set the entire text contents. Just use gtk_text_buffer_insert() to insert new text at the end. Use gtk_text_buffer_get_iter_at_offset() first to get an iterator for the end of the buffer's contents.
UPDATE Oh, and also, you absolutely cannot call GTK+ from multiple threads. It is not a thread-safe library, so don't do that. It's a bit unclear if you do, but it sure sounds like it.

Related

Validating entry for GtkCellRendererText, g_signal_connect gpointer to struct contains garbage

I'm trying to come up with a general purpose library to remove a lot of the set up work for GTK, so that the GTK code can be hidden away neatly. Mostly it works okay. The problem I have is with providing a validator for an edited Cell. If I put the validation code into my tree_cell_edited function then it works fine - but I don't want to do that because that messes with my nicely reusable libraries (they won't be reusable anymore!)
What I tried to do is implement tree_cell_edited as follows:
void tree_cell_edited (GtkCellRendererText *cell, const gchar *path_string, const gchar *new_text, gpointer model_definition_ptr) {
struct model_definition *model_def = (struct model_definition*)model_definition_ptr;
GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(model_def->tableview));
if (model_def->validator_ptr == NULL) {
g_print("Validator is null\n");
}
GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
GtkTreeIter iter;
gint column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column_number"));
gtk_tree_model_get_iter (model, &iter, path);
char *upd_text=(char *)malloc( strlen(new_text) * 2 ); // need to malloc twice the size of the text to allow modification - need a better solution later
strcpy(upd_text, new_text);
if ( ( ((struct model_definition*)model_definition_ptr)->validator_ptr == NULL ) || (*model_def->validator_ptr)(cell, path_string, (char **) &upd_text, model) ) {
gtk_tree_store_set(GTK_TREE_STORE (model), &iter, column, upd_text, -1);
}
free(upd_text);
gtk_tree_path_free (path);
}
Which is set up as follows:
g_signal_connect (cell, "edited", G_CALLBACK (tree_cell_edited), &model_def);
The model_def struct is set up as:
typedef struct model_definition {
bool (*validator_ptr)();
GtkWidget *tableview;
} model_definition;
and
model_definition model_def;
model_def.validator_ptr = &valid_edit;
model_def.tableview = tableview;
where valid_edit is my validation function. When I try this with valid_edit set to NULL then Validator is NULL never gets printed. When I use this style of code I get invalid cast from 'GtkWindow' to 'GtkTreeView' errors some of the time but not all of the time - whereas if I do the more traditional passing of the GtkTreeModel in to tree_cell_edited it all works fine.
Whats the issue here? Do I not have complete freedom over what I pass into whatever function I call with g_signal_connect for an edited cell? Is there something embarrassing and obviously wrong with my code? Is there a better way of calling my validation routine, bearing in mind that I don't want it in my Gtk code (for reasons of reusability).
If it helps, this code needs to be usable on Linux, Windows and macOS - so if it's a clever but platform specific trick then it isn't going to work.

Segfault during GTK_TOGGLE_BUTTON() macro. Pointer values changed after changing checkbox state

I am trying to create a GUI program using Glade and GTK+ 3 to write set and write configuration data to a custom microchip. I am trying to implement a feature to save/load previous configurations into my GUI. After saving to a file and loading, some of my callback functions now cause a segfault. One example being a callback for a "global enable" checkbox.
I am currently trying to read data from my file and using that data to update the state of the GUI (as well as global variables used for configuring the IC). I update my checkbox as follows:
/* Global widget handles */
GtkWidget* GEN_CB_h;
GtkWidget* Neg_Pol_CB_h;
GtkWidget* Save_File_Name_Box_h;
GtkWidget* Save_File_Button_h:
GtkWidget* Load_File_Button_h;
//more widgets here
/* Global configuration variables */
char gen;
char neg_pol;
//more variables here
/* Write data to a configuration file. */
void on_Save_Config_Button_clicked()
{
GtkEntry* save_file = GTK_ENTRY(Save_File_Box_h);
const gchar* filename = gtk_entry_get_text(save_file);
FILE* fd = fopen((const char*)filename, "w");
if(!fd)
{
perror("Failed to open file\n");
exit(EXIT_FAILURE);
}
fwrite(&gen, sizeof(gen), 1, fd);
fwrite(&neg_pol, sizeof(neg_pol), 1, fd);
//more variables written here
fclose(fd);
g_printf("File saved to: %s\n", filename);
}
/* Load data from a stored configuration file */
void on_Load_File_Button_selection_changed()
{
GtkFileChooser* file = GTK_FILE_CHOOSER(Load_File_Box_h);
gchar* filename = gtk_file_chooser_get_filename(file);
FILE* fd = fopen((const char*)filename, "r");
if(!fd)
{
perror("Failed to open file\n");
exit(EXIT_FAILURE);
}
fread(&gen, sizeof(gen), 1, fd);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GEN_CB_h), gen);
fread(&neg_pol, sizeof(neg_pol), 1, fd);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(neg_pol_cb), neg_pol);
/* read more data and update more GUI elements here */
}
/* Callback for negative polarity bit checkbox */
void on_Neg_Pol_CB_toggled()
{
GtkToggleButton* neg_pol_cb = GTK_TOGGLE_BUTTON(Neg_Pol_CB_h);
neg_pol = (char)gtk_toggle_button_get_active(neg_pol_cb);
printf("Neg pol toggled: %s\n", (neg_pol) ? "ON":"OFF");
}
/* Callback for global enable bit checkbox */
void on_GEN_CB_toggled()
{
printf("gen_cb = %p\n", GEN_CB_h); //print before cast for debugging
GtkToggleButton* gen_cb = GTK_TOGGLE_BUTTON(GEN_CB_h);
gen = (char)gtk_toggle_button_get_active(gen_cb);
printf("GEN toggled: %s\n", (gen) ? "ON":"OFF");
}
int main(int argc, char *argv[])
{
GtkBuilder *builder;
GtkWidget *window;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
gtk_builder_add_from_file (builder, "CFD.glade", NULL);
window = GTK_WIDGET(gtk_builder_get_object(builder, "window_main"));
GEN_CB_h = GTK_WIDGET(gtk_builder_get_object(builder, "GEN_CB"));
Neg_Pol_CB_h = GTK_WIDGET(gtk_builder_get_object(builder, "Neg_Pol_CB"));
Save_File_Box_h = GTK_WIDGET(gtk_builder_get_object(builder, "Save_File_Name_Box"));
Load_File_Box_h = GTK_WIDGET(gtk_builder_get_object(builder, "Load_File_Button"));
Save_File_Button_h = GTK_WIDGET(gtk_builder_get_object(builder, "Save_Config_Button"));
//create more widgets here
gen = 1;
neg_pol = 0; //positive polarity
//set more variables here
gtk_builder_connect_signals(builder, NULL);
printf("connect returned\n");
g_object_unref(builder);
printf("unref returned\n");
gtk_widget_show(window);
printf("show returned\n");
gtk_main();
return 0;
}
The issue I am having is that after loading a new configuration (i.e. triggering my Load_File_Button callback) my GEN_CB callback triggers a segfault on this line:
GtkToggleButton* gen_cb = GTK_TOGGLE_BUTTON(GEN_CB_h);
I used GDB to see what the cause of the segfault was and it reported it as:
Thread 1 "cfd_gui" received signal SIGSEGV, Segmentation fault.
0x00007ffff7395ea0 in g_type_check_instance_cast () from /usr/lib/libgobject-2.0.so.0
I also included a printf statement to check and see if anything was happening to the pointer between loads and found that the pointer value is being modified after this line is executed:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(GEN_CB_h), gen);
I am still new to GTK3 so I am probably using something wrong but have not been able to figure it out. Here are the outputs of my printf debug statements:
gen_cb = 0x55cd1aa04240
GEN toggled: OFF
Loaded configuration from file: /home/borabutt/github/CFD-RPI-Test/src/C/gui/test.dat
gen_cb = 0x55ea7bb68240
The first line shows the pointer to GEN_CB_h before configuration data is loaded. Then as can clearly be seen, the pointer value is changed after the configuration data is loaded. My investigations have shown this change occurs after setting the state of the checkbox as shown above.
I need to be able to update the state of many checkboxes and combo boxes to properly reflect the loaded state from the file. Right now the GUI works completely fine until I load data from a file. Any help and insight is appreciated.
EDIT:
I should note that I included the Neg_Pol_CB callback because this function does not cause a segfault after loading configuration data, while the GEN_CB callback does.
I got it solved. Turns out the code I was editing/building on my laptop was out of date with what was on my github (github code is what I posted here). I thought I pulled but apparently not. I had a line of code where I was writing/reading the wrong data element size to the file:
fwrite(&gen, sizeof(&gen), 11, fd);
...
fread(&gen, sizeof(&gen), 11, fd);
Two mistakes, I was accidentally reading/writing sizeof(&gen) instead of sizeof(gen) and I was also reading/writing 11 data elements instead of one. This caused me to overwrite some other pointers obviously. I'll work on updating all of my callbacks to match the signatures, but it all seems to be working now.

Iterators do not belong to their assigned text buffer

This is a bare bones save_as() function:
gint save_as(GtkWidget *parent, struct buffers B)
{
GtkWidget *file_chooser = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_OK, NULL);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), "Untitled");
gint response = gtk_dialog_run(GTK_DIALOG(file_chooser));
switch(response)
{
case GTK_RESPONSE_OK:
GFile *file = g_file_new_for_path(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)));
GtkTextIter *start;
gtk_text_buffer_get_start(B.buffer0, &start);
GtkTextIter *end;
gtk_text_buffer_get_end(B.buffer0, &end);
// program abnormally terminates on the following line
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
g_file_replace_contents(file, contents, strlen(contents), NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
g_free(contents);
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_OK;
break;
case GTK_RESPONSE_CANCEL:
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
// user pressed X
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
This is pretty much all we need to diagnose and fix the problem.
Here is the full message I get when I try to click on File -> Save As... and then on Save:
(test:4478): Gtk-WARNING **: 11:56:20.184: Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixbufs/widgets in the buffer have been modified since the iterator was created.
You must use marks, character numbers, or line numbers to preserve a position across buffer modifications.
You can apply tags and insert marks without invalidating your iterators, but any mutation that affects 'indexable' buffer contents (contents that can be referred to by character offset) will invalidate all outstanding iterators
(test:4478): Gtk-CRITICAL **: 11:56:20.184: gtk_text_buffer_get_text: assertion 'gtk_text_iter_get_buffer (start) == buffer' failed
Segmentation fault (core dumped)
For some reason, it seems that the iterators start and end do not belong to B.buffer0. Apart from GTK+3 documentation, I also followed answers to this question as a guide.
Why is this happening and how it can be fixed?
I also tried changing the following lines:
gtk_text_buffer_get_start_iter(B.buffer0, &start); to gtk_text_buffer_get_start_iter(B.buffer0, start);
gtk_text_buffer_get_start_iter(B.buffer0, &end); to gtk_text_buffer_get_start_iter(B.buffer0, end);
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE); to gchar *contents = gtk_text_buffer_get_text(B.buffer0, start, end, FALSE);
The only error I get is:
Segmentation fault (core dumped)
This is also supposed to be the correct way to send arguments according to the documentation.
Also, I tried replacing contents = gtk_text_buffer_get_text(B.buffer0, start, end, FALSE); with contents = gtk_text_iter_get_text(start, end); but I get the same error.
I also noticed that I get two warnings during compiling after applying those changes:
src/save.c:96:5: warning: ‘start’ may be used uninitialized in this function [-Wmaybe-uninitialized]
gtk_text_buffer_get_start_iter(B.buffer0, start);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/save.c:98:5: warning: ‘end’ may be used uninitialized in this function [-Wmaybe-uninitialized]
gtk_text_buffer_get_end_iter(B.buffer0, end);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This obviously means that gtk_text_buffer_get_start/end_iter() fails to associate start and end with B.buffer0, but why? It says that start and end are passed uninitialized, but isn't that normal since I'm initializing them now? Or I should do something before that?
Definition of struct buffer is in a file called buffer.h. Here it is (not the whole file, just the struct definition):
struct buffers
{
GtkTextBuffer *buffer0;
GtkTextBuffer *buffer1;
GtkTextBuffer *buffer2;
};
The instance I use is:
struct buffers Buffer;
This is a global variable, so buffer.h is included in every file that uses Buffer. And for the same purpose (globality), of course it is declared extern struct buffers Buffer; in the header file associated with the source file in which it is defined.
This buffer is passed to a function save_as() like this:
gint response = save_as(main_window, Buffer);
In case you're wondering why I just don't use global variable instead of passing it as a parameter, it is because I can just pass any variable of type struct buffers to a save_file() and let it do the job, instead of having another function for each struct buffers variable.
I tried initializing iterators upon their definition:
GtkTextIter *start = NULL;
GtkTextIter *end = NULL;
Everything compiles without any warnings. Unfortunately, this doesn't solve the problem. Upon clicking File -> Save As and then Save, the program exits and I get messages:
(test:4081): Gtk-CRITICAL **: 19:48:29.843: gtk_text_buffer_get_start_iter: assertion 'iter != NULL' failed
(test:4081): Gtk-CRITICAL **: 19:48:29.844: gtk_text_buffer_get_end_iter: assertion 'iter != NULL' failed
(test:4081): Gtk-CRITICAL **: 19:48:29.844: gtk_text_buffer_get_text: assertion 'start != NULL' failed
Why is it even important that my start and end iterators are NULL at least in the first two cases? Aren't those two functions supposed to set start and end iterators to the start and the end of a file, respectively? Therefore, why does it matter to what iterators are set prior to that?
Finally solved it. It looks like that GtkTextIter is a little bit different than most GTK types.
Here is how it should be done:
gint save_as(GtkWidget *parent, struct buffers B)
{
GtkWidget *file_chooser = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_OK, NULL);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), "Untitled");
gint response = gtk_dialog_run(GTK_DIALOG(file_chooser));
switch(response)
{
case GTK_RESPONSE_OK:
GFile *file = g_file_new_for_path(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)));
GtkTextIter start;
gtk_text_buffer_get_start(B.buffer0, &start);
GtkTextIter end;
gtk_text_buffer_get_end(B.buffer0, &end);
// program abnormally terminates on the following line
gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
g_file_replace_contents(file, contents, strlen(contents), NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
g_free(contents);
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_OK;
break;
case GTK_RESPONSE_CANCEL:
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
// user pressed X
gtk_widget_destroy(file_chooser);
return GTK_RESPONSE_CANCEL;
}
As you can see, the only difference in this code and the code from my question are the following two lines:
Wrong
GtkTextIter *start;
GtkTextIter *end;
Right
GtkTextIter start;
GtkTextIter end;
Most of the GTK types want you to make them pointers, but this one is an exception.

Passing an array to a function - Different values - Segfault

I have the following code:
gpointer w[3];
GtkWidget *menu_item = gtk_menu_item_new();
w[0] = menu_item;
menu_item = gtk_menu_item_new();
w[1] = menu_item;
GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
w[2] = buffer;
This is all good till now. Let's now connect a signal:
g_signal_connect(w[0], "activate", G_CALLBACK(runner), w);
runner function is declared as:
void runner(gpointer root, gpointer w[]);
Testing the values of w array before entering runner and while in it shows that they (the values) are different. I need them to be the same. How can I accomplish that, and why they aren't identical? Also, segfault occurs.
I created a small program that is bare bones of the original one and that is supposed to recreate the conditions such that the problem occurs. Oddly enough, it runs fine.
#include <gtk/gtk.h>
void carry(gpointer root, gpointer a[])
{
g_print("\n");
g_print("%d\n", root);
g_print("%d\n", a[0]);
g_print("%d\n", a[1]);
g_print("%d\n", a[2]);
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
GtkWidget *menu_item;
GtkTextBuffer *buffer;
gpointer abc[3];
menu_item = gtk_menu_item_new();
abc[0] = menu_item;
g_print("%d\t%d\n", menu_item, abc[0]);
menu_item = gtk_menu_item_new();
abc[1] = menu_item;
g_print("%d\t%d\n", menu_item, abc[1]);
buffer = gtk_text_buffer_new(NULL);
abc[2] = buffer;
g_print("%d\t%d\n", buffer, abc[2]);
g_signal_connect(abc[2], "modified-changed", G_CALLBACK(carry), abc);
gtk_text_buffer_set_modified(abc[2], TRUE);
gtk_main();
return 0;
}
Which means that something else is problematic. I'll try something else now, like commenting lines and leaving only the relevant ones.
I didn't comment any lines yet, but I tried putting g_print in both the caller and the callee.
This is an output:
1162863440 1162863440
1162864736 1162864736
1163320992 1163320992
1162863440
-2
1162668992
973486176
The first three lines compare the original values with their copies in the array (in the sense of g_print("%d\t%d\n", menu_item, abc[0]); from the code above). As you can see, everything is assigned correctly. After a new line, we check those same values in the callee. root, the first parameter, always has the correct value. So there's no problem with that. abc[0] in the callee always has the value of -2. Seriously, every time I run the program it is -2. Other two (abc[1] and abc[2]) always have some garbage random values, but they change every time I run the program unlike abc[0].
I hope this will help in diagnosing and fixing the problem.
I tried passing both abc[0] and abc normally through a function (func(arg0, arg1, ...) instead of using g_signal_connect()) and there is no problem whatsoever.
This all can mean only one thing: g_signal_connect is messing with my values. It changes them for some unknown reason.
I guess I'll have to use a struct.
You're not supposed to use gpointers everywhere. A gpointer is a void *, so you're pretty much disabling all the type checking the compiler could do for you. Use GtkWidget * instead, and do proper casts using G_OBJECT(), GTK_TEXT_BUFFER() etc. macros.
You should also use typed callback arguments, as they appear in the documentation for each signal. For example for the activate signal:
void
user_function (GtkMenuItem *menuitem,
gpointer user_data)
And if you want to pass several items in the user-data field, pass a pointer or pointer to a structure instead of an array of pointers.
And if you have a segfault, well, just use a debugger to check where the problem is.

Dialog creation and destruction loop increases memory usage

I wrote a simple program module to ask a user for a profile name. To do that I create windoww with entry widget, and two buttons (Ok and Cancel) organized in a grid. When user enters a profile name that already exists it informs him of that fact by creating dialog with "ok" button, and after he presses it, it goes back to picking a profile name (the window was not hidden nor destroyed in the meantime). The problem is that when I create a profile, and then spam the ok button (by placing something heavy on enter key and going to make tea) on both profile name chooser and the dialog (making a simple loop with creation and destruction of a dialog) the memory usage of the program increases.
TL;DR
Simply creating and destroying gtk window (and dialog) seems to cause a memory leak. Leaving app in a loop made it increase it's memory usage by ~1900% (from 10mb to 200mb).
No, I didn't test for memory leaks using apps designed for it.
Yes, I've set G_SLICE=always-malloc.
Yes, there is another thread running in the background of the program (but I'm sure it doesn't cause any leaks)
I can post screens from Windows Performance Monitor if you want more info on what happens in the memory.
The question is - is it a memory leak caused by me, or is it GTK's fault (I heard it has a lazy policy of memory management, but after some time memory usage drops from 200mb to 140mb and stays there)?
Here's the code:
// This callback racts to the ok and cancel buttons. If input was correcs
// or the user pressed cancel it destroys the window. Else it show error
// prompt. The showing error prompt seems to be the problem here.
void pickNameButtCB(GtkWidget *button, gpointer *call)
{
GtkWidget *window = gtk_widget_get_toplevel(button);
if( *((char*)call) == 'k')
{
GList *entryBase = gtk_container_get_children(GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(window)))), *entry = entryBase;
for(size_t i=g_list_length(entry); i>0 && !GTK_IS_ENTRY(entry->data); --i)
entry = g_list_next(entry);
if(gtk_entry_get_text_length(GTK_ENTRY(entry->data)) > 0)
{
const char *temp = gtk_entry_get_text(GTK_ENTRY(entry->data));
char path[266];
strcpy(path, settingsDir);
strcat(path, temp);
strcat(path, ".prof");
if(settProfExists(path))
{
g_list_free(entryBase);
showError(GTK_WINDOW(window), GTK_MESSAGE_ERROR, "Profile with that name already exists!");
return;
}
// nothing here executes as well
}
else
{
/** doesn't execute when the memory leak happens */
}
g_list_free(entryBase);
}
gtk_widget_destroy(window);
gtk_main_quit();
}
void showError(GtkWindow *parent, GtkMessageType type, const char *str)
{
GtkWidget *window = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, str);
g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window);
gtk_dialog_run(GTK_DIALOG(window));
}
bool settProfExists(const char *path)
{
if(fileExists(path))
return true;
return false;
}
bool fileExists(const char *path)
{
struct stat info;
errno = 0;
if((stat(path, &info)) != 0)
if(errno == ENOENT)
return false;
return true;
}
Your code is too incomplete to find the root cause.
This line especially is doesn't make sense. We're missing the definition of entry and you're trying to do several operations on the same line (*entry = entryBase;).
GList *entryBase = gtk_container_get_children(GTK_CONTAINER(gtk_bin_get_child(GTK_BIN(window)))), *entry = entryBase;
The leak may however be in functions you call for which you don't provide the code (settProfExists, newProfile). So please provide a minimal compilable example that reproduces the problem.
As for the style, there are several errors: you're traversing a list the way you'd do with an array. See how is implemented GList and you'll see that this is silly (hint: look at entry->prev and entry->next):
for(size_t i=g_list_length(entry); i>0 && !GTK_IS_ENTRY(entry->data); --i)
entry = g_list_next(entry);
Then an architectural problem: don't try to inspect your UI to guess where are the widgets, just create a structure, and pass you call parameter and the GtkEntry you're interested in, in a structure with the gpointer data argument of the callback.
You should also not make paths by hand: you're using a fixed length array that may not be long enough. Use the portable g_build_path provided by the GLib (and free the path with g_free after use), that will handle for you Windows/Linux directories separators.
For your dialog, as you run gtk_run_dialog, you may just directly call gtk_destroy_widget afterwards instead of connecting a signal:
GtkWidget *window = gtk_message_dialog_new(parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, str);
gtk_dialog_run(GTK_DIALOG(window));
gtk_widget_destroy(window);

Resources