Search code examples
cstructgtk

How do I parse multiple GtkWidgets in a callback function?


I have two GtkWidgets that I want to have access to when I am inside a call back function

    // (in activate)
    GtkWidget *input;
    GtkWidget *output;
    input = gtk_entry_new();
    output = gtk_entry_new();

So what I do is create a global struct that contains a pointer to each widget:

struct io_entries {
    GtkWidget *input_field;
    GtkWidget *output_field;
};

Then I create a dynamically allocated struct in main so that I can signal connect it to a call back function

    // (in activate)
    struct io_entries *data = malloc(sizeof(struct io_entries));
    data->input_field = input;
    data->output_field = output;
    
    g_signal_connect(button, "clicked", G_CALLBACK (my_function), data);
    free(data)

And finally I adjust the arguments of the callback function so that I can access the data inside of the struct:

static void my_function(GtkWidget *widget, struct io_entries *data) {
    // Now I can access my input AND output entry widget with data->x_field
    unsigned int input_length = (guint)gtk_entry_get_text_length(GTK_ENTRY (data->input_field));

    // ...
}

This, for some probably obvious reason that my amateur skills can't see, creates some undefined behavior where the program will compile normally and run perfectly fine but randomly crash out of every 5 runs with identical input data, sometimes giving Windows error codes, or sometimes creating GTK warnings:

GLib-GObject-WARNING **: 13:59:11.034: invalid unclassed pointer in cast to 'GtkEntry'

So my question is, what am I doing wrong?

Is it that I am changing the standard signature of my_function(GtkWidget *widget, gpointer data)? (because if I don't it doesn't let me compile.)

Is it that I am incorrectly casting with GTK_ENTRY(data->input_field)?

Additionally, can I somehow avoid using a global struct?

Thanks a lot in advance.

.

EDIT: I forgot that in main I only have:

int main(int argc, char **argv) {
    GtkApplication *app;
    int status;

    app = gtk_application_new("example.com", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run(G_APPLICATION (app), argc, argv);
    printf("Program has ended\n");
    g_object_unref(app);

    return status;
}

So adding to the answer that I got, the solution would be to create a global pointer to that struct:

struct io_entries *io_entries_pointer;

In activate:

    struct io_entries *data = malloc(sizeof(struct io_entries));
    io_entries_pointer = data; // So I can free it outside of the scope

And finally in main I would add this after g_application_run():

free(io_entries_pointer);

Solution

  • You are quite reasonably creating your structure with the required fields:

        // (in main)
        struct io_entries *data = malloc(sizeof(struct io_entries));
        data->input_field = input;
        data->output_field = output;
        
        g_signal_connect(button, "clicked", G_CALLBACK (my_function), data);
    

    At this point, GTK has stored a copy of your pointer, data. Note that it has not taken a copy of your structure, only the pointer to it.

    You then do this:

        free(data);
    

    Now, data is an invalid pointer. Other things that call malloc() and friends are likely to get the memory that your structure was in, and can overwrite it.

    Later, your callback function is called, and you dereference data, which is an invalid pointer. This will invoke Undefined Behaviour.

    Solution: do not call free(data); until such a time as you are guaranteed not to get any more callbacks (e.g. just before the program exits).