Home > Software design >  Segmentation fault while accessing other GTK widgets in a callback
Segmentation fault while accessing other GTK widgets in a callback

Time:01-11

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;
}

CodePudding user response:

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;

  •  Tags:  
  • Related