0

To access other widgets in a callback, I packed them in a structure defined as follows:

typedef struct SettingsModel
{
    bool SyncEn;
    int DeviceId;
    int MeasurementId;
    GtkWidget *measurement_type_box;
    GtkWidget *sync_switch;
    GtkWidget *device_type_box;
} SettingsModel;

The aim is to update the settings when the save button is clicked. The relevant code snippets:

static void click_save(GtkButton *self, gpointer user_data)
{
    SettingsModel *local_data = user_data;
    local_data->DeviceId = gtk_combo_box_get_active(GTK_COMBO_BOX(local_data->device_type_box));
    local_data->MeasurementId = gtk_combo_box_get_active(GTK_COMBO_BOX(local_data->measurement_type_box));
    local_data->SyncEn = gtk_switch_get_active(GTK_SWITCH(local_data->sync_switch));
}
save_settings_button = gtk_button_new_with_label("Save");
reset_settings_button = gtk_button_new_with_label("Reset");
SettingsModel settings;
settings.device_type_box = device_type_box; 
settings.measurement_type_box = measurement_type_box;
settings.sync_switch = sync_enabled_switch;

g_signal_connect(save_settings_button, "clicked", G_CALLBACK(click_save), &settings);
g_signal_connect(reset_settings_button, "clicked", G_CALLBACK(click_reset), &settings);

However, when I click on the save button, I receive the following errors:

(gui:28351): GLib-GObject-WARNING **: 16:52:26.673: invalid cast from 'GdkButtonEvent' to 'GtkComboBox'

(gui:28351): Gtk-CRITICAL **: 16:52:26.673: gtk_combo_box_get_active: assertion 'GTK_IS_COMBO_BOX (combo_box)' failed
zsh: segmentation fault  ./gui

It seems that either the structure was initialized wrongly or GTK somehow thinks that the callback should take a GdkButtonEvent?

Minimal reproducible example here:

#include <gtk/gtk.h>

enum 
{
    COL_ID = 0,
    COL_NAME,
    NUM_COLS
};

typedef struct SettingsModel
{
    bool SyncEn;
    int DeviceId;
    int MeasurementId;
    GtkWidget *measurement_type_box;
    GtkWidget *sync_switch;
    GtkWidget *device_type_box;
} SettingsModel;

static void click_save(GtkButton *self, gpointer user_data)
{
    SettingsModel *local_data = user_data;
    local_data->DeviceId = gtk_combo_box_get_active(GTK_COMBO_BOX(local_data->device_type_box));
    local_data->MeasurementId = gtk_combo_box_get_active(GTK_COMBO_BOX(local_data->measurement_type_box));
    local_data->SyncEn = gtk_switch_get_active(GTK_SWITCH(local_data->sync_switch));
}

static void click_reset(GtkButton *btn_reset, gpointer user_data)
{
    SettingsModel *local_data = user_data;
    local_data->DeviceId = 0;
    local_data->MeasurementId = 0;
    local_data->SyncEn = false;
    gtk_switch_set_active(GTK_SWITCH(local_data->sync_switch), FALSE);
    gtk_combo_box_set_active(GTK_COMBO_BOX(local_data->device_type_box), 0);
    gtk_combo_box_set_active(GTK_COMBO_BOX(local_data->measurement_type_box), 0);
}

static void app_activate(GApplication *app, gpointer *user_data)
{
    GtkWidget *win;
    GtkWidget *grid;

    g_assert(GTK_IS_APPLICATION(app));
    win = gtk_application_window_new(GTK_APPLICATION(app));
    grid = gtk_grid_new();

    GtkWidget *device_type_box;
    GtkWidget *measurement_type_box;
    GtkWidget *save_settings_button;
    GtkWidget *reset_settings_button;
    GtkWidget *sync_enabled_switch;
    GtkListStore *list_store_device;
    GtkListStore *list_store_measurement;
    GtkCellRenderer *column;

    list_store_device = gtk_list_store_new(NUM_COLS, G_TYPE_INT, G_TYPE_STRING);
    gtk_list_store_insert_with_values(list_store_device, NULL, -1, COL_ID, 0, COL_NAME, "foo", -1);
    device_type_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store_device));
    g_object_unref(list_store_device);
    column = gtk_cell_renderer_text_new();
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(device_type_box), column, TRUE);
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(device_type_box), column,
                                "text", 1,
                                NULL);
    gtk_combo_box_set_active(GTK_COMBO_BOX(device_type_box), 0);
    
    list_store_measurement = gtk_list_store_new(NUM_COLS, G_TYPE_INT, G_TYPE_STRING);
    gtk_list_store_insert_with_values(list_store_measurement, NULL, -1, COL_ID, 0, COL_NAME, "foo", -1);
    gtk_list_store_insert_with_values(list_store_measurement, NULL, -1, COL_ID, 0, COL_NAME, "bar", -1);
    gtk_list_store_insert_with_values(list_store_measurement, NULL, -1, COL_ID, 0, COL_NAME, "something", -1);
    measurement_type_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(list_store_measurement));
    g_object_unref(list_store_measurement);
    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(measurement_type_box), column, TRUE);
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(measurement_type_box), column,
                                "text", 1,
                                NULL);
    gtk_combo_box_set_active(GTK_COMBO_BOX(measurement_type_box), 2);

    save_settings_button = gtk_button_new_with_label("Save");
    reset_settings_button = gtk_button_new_with_label("Reset");

    sync_enabled_switch = gtk_switch_new();

    SettingsModel settings;
    settings.device_type_box = device_type_box; 
    settings.measurement_type_box = measurement_type_box;
    settings.sync_switch = sync_enabled_switch;

    g_signal_connect(save_settings_button, "clicked", G_CALLBACK(click_save), &settings);
    g_signal_connect(reset_settings_button, "clicked", G_CALLBACK(click_reset), &settings);

    gtk_grid_attach(GTK_GRID(grid), device_type_box, 1, 1, 1, 1);    
    gtk_grid_attach(GTK_GRID(grid), measurement_type_box, 1, 2, 1, 1);  
    gtk_grid_attach(GTK_GRID(grid), sync_enabled_switch, 1, 3, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), reset_settings_button, 4, 4, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), save_settings_button, 5, 4, 1, 1);

    gtk_window_set_child(GTK_WINDOW(win), grid);

    gtk_widget_show(win);
}

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

    app = gtk_application_new("simon.app", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(app_activate), NULL);
    stat = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);
    return stat;
}
2
  • My interpretation of the error message is that local_data->device_type_box or local_data->measurement_type_box is a GdkButtonEvent instead of a GtkComboBox. Maybe you initialize the structure with wrong widget types or maybe the pointer to the structure is wrong. Please edit your question and provide a minimal reproducible example. Commented Jan 10, 2022 at 17:07
  • Hi, I've added the example to the question. Thanks in advance! @Bodo Commented Jan 10, 2022 at 17:34

1 Answer 1

0

You are accessing illegal memory because you are passing the address of a non-static object to your callback:

static void app_activate(GApplication *app, gpointer *user_data)
{
...
    SettingsModel settings;

    g_signal_connect(save_settings_button, "clicked", G_CALLBACK(click_save), &settings);
    g_signal_connect(reset_settings_button, "clicked", G_CALLBACK(click_reset), &settings);
...
}

When your signal handlers are called, the function app_activate has already returned to the caller and settings is no longer valid. You must provide address of some static memory object or you need to dynamically allocate memory for your settings.

I would suggest to change definition of settings to static SettingsModel settings;

Sign up to request clarification or add additional context in comments.

6 Comments

That worked wonders! Thanks for pointing that out. I totally forgot that settings was initialized in a function.
@Simon48 That does not mean that is the right way to resolve your problem. Your code is almost always wrong if you need to use static......almost always and NOT always.
@Michi In what way would another option to make the data be available during whole lifetime be more appropriate? Do you see any benefit of making that variable global or use dynamic memory allocation?
I am definitely not against the static keyword, but the program flow should not need to use static to work. This is not how GTK should work at all.
@Michi you might provide an own answer with an improved mechanism to provide required data to the signal handlers and other callbacks
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.