GTK C segv: добавление нового сигнала вызывает segv


Я получаю ошибку sigsegv с введением нового сигнала коммутатора в существующий тестовый графический интерфейс.

Пользовательский интерфейс имеет 3 возможных источника событий, два блока событий и один переключатель. При щелчке на полях событий они ведут себя нормально, однако если переключатель переключен, то щелчок на одном из полей событий приводит к segv (поведение 1).

Кроме того, при запуске из new, переключение переключателя сначала, а затем щелчок на поле события также приводит к segv (поведение 2). Segv для обоих происходит всегда в одном и том же месте (если использовать один из полей событий в качестве примера) следующим образом:

if (((interface *) toggle)->eth0->ip_enabled) {

Самое простое проявление, которое я мог бы придумать в терминах кода, заключается в следующем (извините за включение):

/*
 * STRIPPED DOWN VERSION
 */
#include <arpa/inet.h>
#include <gtk/gtk.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


#define IPV4_ENABLED                1
#define IPV4_DISABLED               0
#define SELECTED                    1

typedef struct {
    int ip_enabled;
} net;

typedef struct {
    net *eth0;
    net *wlan0;
    int type;
} interface;

typedef enum {
    IT_ETH0,
    IT_WLAN0,
    IT_UNASSIGNED = 999,
} interface_type;

typedef struct {
    int active;
    interface *port;
    int debug;
} test_parameters;

void wired (net *eth0);

void wifi (net *wlan0);

int main (int argc, char *argv[]) {

    GtkBuilder *builder;
    GtkWidget *window;
    GError *gtk_error = NULL;

    net *eth0 = g_slice_new (net);
    net *wlan0 = g_slice_new (net);
    interface *toggle = g_slice_new (interface);

    test_parameters *test = g_slice_new (test_parameters);

    test->port->type == IT_UNASSIGNED;

        /*Add interfaces to struct "interface" */
    toggle->eth0 = eth0;
    toggle->wlan0 = wlan0;

    toggle->eth0->ip_enabled = 0;
    toggle->wlan0->ip_enabled = 0;

    if (NULL == toggle->eth0)
        fprintf (stdout, "%sn", "eth0 is null ");

    if (NULL == toggle->wlan0)
        fprintf (stdout, "%sn", "wlan0 is null ");


    gtk_init (&argc, &argv);

    builder = gtk_builder_new ();
    if (!gtk_builder_add_from_file (builder, "glade/window_main.glade", NULL)) {
        fprintf (stdout, "%s%sn", "Error loading file with the reason: ", gtk_error->message);
        g_free (gtk_error);
        exit (EXIT_FAILURE);
    }

    window = GTK_WIDGET (gtk_builder_get_object (builder, "window_main"));

    gtk_builder_connect_signals (builder, toggle);
    gtk_builder_connect_signals (builder, test);

    g_object_unref (builder);

    wired (eth0);
    wifi (wlan0);
    gtk_widget_show (window);

    gtk_main ();

    g_slice_free (net, eth0);
    g_slice_free (net, wlan0);
    g_slice_free (interface, toggle);

    return 0;

}

void wifi (net *wlan0) {

    if (1) {
        wlan0->ip_enabled = IPV4_ENABLED;
    }

}

void wired (net *eth0) {

    if (1) {
        eth0->ip_enabled = IPV4_ENABLED;
    }

}

G_MODULE_EXPORT gboolean on_eventbox_image_wired_button_press_event (GtkWidget *widget,
    GdkEvent *event, gpointer toggle) {

    if (((interface *) toggle)->eth0 == NULL) {
        printf("%sn", "WIRED: eth0 IS NULL - REMOVE ME");
    }

    if (((interface *) toggle) == NULL) {
        printf("%sn", "WIRED: toggle IS NULL - REMOVE ME");
    }

    if (((interface *) toggle)->eth0->ip_enabled) {
        printf("%sn", "WIRED: eth0 is ip enabled - REMOVE ME");
        ((interface *) toggle)->type = IT_ETH0;
        if (((interface *) toggle)->wlan0->ip_enabled) {
            printf("%sn", "WIRED: wlan0 is ip enabled, adjust - REMOVE ME");
        }

    }
    return 0;
}

G_MODULE_EXPORT gboolean on_eventbox_image_wifi_button_press_event (GtkWidget *widget,
    GdkEvent *event, gpointer toggle) {

    /* Check to see if we have IP address so we can change the colour otherwise leave it red */
    if (((interface *) toggle)->wlan0->ip_enabled) {
        printf("%sn", "WLAN has IP enabled");
        ((interface *) toggle)->type = IT_ETH0;
        if (((interface *) toggle)->eth0->ip_enabled) {
            printf("%sn", "WLAN: eth0 is ip enabled, adjust - REMOVE ME");
        }

    }

    return 0;       
}

G_MODULE_EXPORT gboolean on_switch_test_on_state_set (GtkSwitch *widget,
    gboolean state, gpointer test) {

    (((test_parameters *) test)->active) = state;

    fprintf (stdout, "%s%in", "Signal fired and the state is ", state);

    return 0;

}

void on_window_main_destroy () {

    gtk_main_quit ();
}

А gdb bt-это:

(gdb) run
Starting program: /home/j2/simplified_example/simplified_example 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7fffed0c4700 (LWP 6979)]
[New Thread 0x7fffec8c3700 (LWP 6980)]
WIRED: eth0 is ip enabled - REMOVE ME
WIRED: wlan0 is ip enabled, adjust - REMOVE ME
Signal fired and the state is 1

Thread 1 "simplified_exam" received signal SIGSEGV, Segmentation fault.
0x0000000000401110 in on_eventbox_image_wired_button_press_event (widget=0x6a7260 [GtkEventBox], event=0x994cb0, toggle=0x624800)
    at src/simplified_example1.c:134
134     if (((interface *) toggle)->eth0->ip_enabled) {
(gdb) bt
#0  0x0000000000401110 in on_eventbox_image_wired_button_press_event (widget=0x6a7260 [GtkEventBox], event=0x994cb0, toggle=0x624800)
    at src/simplified_example1.c:134
#5  0x00007ffff728008f in <emit signal ??? on instance 0x6a7260 [GtkEventBox]> (instance=instance@entry=0x6a7260, signal_id=<optimised out>, detail=detail@entry=0) at /build/glib2.0-prJhLS/glib2.0-2.48.2/./gobject/gsignal.c:3441
    #1  0x00007ffff76bafac in _gtk_marshal_BOOLEAN__BOXED (closure=0x6ffe50, return_value=0x7fffffffd940, n_param_values=<optimised out>, param_values=0x7fffffffd9a0, invocation_hint=<optimised out>, marshal_data=<optimised out>) at /build/gtk+3.0-2Ut_nl/gtk+3.0-3.18.9/./gtk/gtkmarshalers.c:86
    #2  0x00007ffff7264fa5 in g_closure_invoke (closure=0x6ffe50, return_value=return_value@entry=0x7fffffffd940, n_param_values=2, param_values=param_values@entry=0x7fffffffd9a0, invocation_hint=invocation_hint@entry=0x7fffffffd920) at /build/glib2.0-prJhLS/glib2.0-2.48.2/./gobject/gclosure.c:804
    #3  0x00007ffff7276fc1 in signal_emit_unlocked_R (node=node@entry=0x653b40, detail=detail@entry=0, instance=instance@entry=0x6a7260, emission_return=emission_return@entry=0x7fffffffdab0, instance_and_params=instance_and_params@entry=0x7fffffffd9a0)
    at /build/glib2.0-prJhLS/glib2.0-2.48.2/./gobject/gsignal.c:3629
    #4  0x00007ffff727f7f9 in g_signal_emit_valist (instance=<optimised out>, signal_id=<optimised out>, detail=<optimised out>, var_args=var_args@entry=0x7fffffffdb60) at /build/glib2.0-prJhLS/glib2.0-2.48.2/./gobject/gsignal.c:3395
#6  0x00007ffff77f8c3c in gtk_widget_event_internal (widget=0x6a7260 [GtkEventBox], event=0x994cb0)
    at /build/gtk+3.0-2Ut_nl/gtk+3.0-3.18.9/./gtk/gtkwidget.c:7692
#7  0x00007ffff76b83be in propagate_event (topmost=<optimised out>, event=<optimised out>, widget=0x6a7260 [GtkEventBox])
    at /build/gtk+3.0-2Ut_nl/gtk+3.0-3.18.9/./gtk/gtkmain.c:2527
#8  0x00007ffff76b83be in propagate_event (widget=<optimised out>, event=0x994cb0, captured=<optimised out>, topmost=0x0)
    at /build/gtk+3.0-2Ut_nl/gtk+3.0-3.18.9/./gtk/gtkmain.c:2629
#9  0x00007ffff76ba1bc in gtk_main_do_event (event=0x994cb0) at /build/gtk+3.0-2Ut_nl/gtk+3.0-3.18.9/./gtk/gtkmain.c:1850
#10 0x00007ffff66dcd92 in gdk_event_source_dispatch (source=<optimised out>, callback=<optimised out>, user_data=<optimised out>)
    at /build/gtk+3.0-2Ut_nl/gtk+3.0-3.18.9/./gdk/x11/gdkeventsource.c:369
#11 0x00007ffff6f8e197 in g_main_context_dispatch (context=0x640980) at /build/glib2.0-prJhLS/glib2.0-2.48.2/./glib/gmain.c:3154
---Type <return> to continue, or q <return> to quit---
#12 0x00007ffff6f8e197 in g_main_context_dispatch (context=context@entry=0x640980) at /build/glib2.0-prJhLS/glib2.0-2.48.2/./glib/gmain.c:3769
#13 0x00007ffff6f8e3f0 in g_main_context_iterate (context=0x640980, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimised out>)
    at /build/glib2.0-prJhLS/glib2.0-2.48.2/./glib/gmain.c:3840
#14 0x00007ffff6f8e712 in g_main_loop_run (loop=0x8d14b0) at /build/glib2.0-prJhLS/glib2.0-2.48.2/./glib/gmain.c:4034
#15 0x00007ffff76b9395 in gtk_main () at /build/gtk+3.0-2Ut_nl/gtk+3.0-3.18.9/./gtk/gtkmain.c:1241
#16 0x0000000000401065 in main (argc=1, argv=0x7fffffffdfd8) at src/simplified_example1.c:97
(gdb) 
(gdb) 
(gdb) 
Пожалуйста, заранее благодарю вас за любую помощь в этом вопросе.

Edit: добавлен файл glade по запросу-он упрощен для целей тестирования:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.12"/>
  <object class="GtkWindow" id="window_main">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Main Window</property>
    <property name="default_width">640</property>
    <property name="default_height">480</property>
    <property name="decorated">False</property>
    <signal name="destroy" handler="on_window_main_destroy" swapped="no"/>
    <child>
      <object class="GtkFixed" id="fixed_background">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <child>
          <object class="GtkImage" id="image_background">
            <property name="name">4</property>
            <property name="width_request">640</property>
            <property name="height_request">480</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="stock">gtk-missing-image</property>
          </object>
        </child>
        <child>
          <object class="GtkEventBox" id="eventbox_image_wired">
            <property name="width_request">100</property>
            <property name="height_request">80</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="visible_window">False</property>
            <property name="above_child">True</property>
            <signal name="button-press-event" handler="on_eventbox_image_wired_button_press_event" swapped="no"/>
            <child>
              <object class="GtkImage" id="image_wired">
                <property name="width_request">100</property>
                <property name="height_request">80</property>
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="xalign">0</property>
                <property name="yalign">0</property>
                <property name="icon_name">applications-graphics</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="x">35</property>
            <property name="y">20</property>
          </packing>
        </child>
        <child>
          <object class="GtkEventBox" id="eventbox_image_wifi">
            <property name="width_request">100</property>
            <property name="height_request">80</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="visible_window">False</property>
            <property name="above_child">True</property>
            <signal name="button-press-event" handler="on_eventbox_image_wifi_button_press_event" swapped="no"/>
            <child>
              <object class="GtkImage" id="image_wifi">
                <property name="width_request">101</property>
                <property name="height_request">71</property>
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="xalign">0</property>
                <property name="yalign">0</property>
                <property name="icon_name">accessories-text-editor</property>
              </object>
            </child>
          </object>
          <packing>
            <property name="x">200</property>
            <property name="y">20</property>
          </packing>
        </child>
        <child>
          <object class="GtkGrid" id="grid_test_parameter">
            <property name="width_request">582</property>
            <property name="height_request">116</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="row_spacing">10</property>
            <property name="column_spacing">4</property>
            <child>
              <object class="GtkSwitch" id="switch_test_on">
                <property name="use_action_appearance">True</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <signal name="state-set" handler="on_switch_test_on_state_set" swapped="no"/>
              </object>
              <packing>
                <property name="left_attach">0</property>
                <property name="top_attach">0</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="x">15</property>
            <property name="y">245</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>
1 2

1 ответ:

Проблема заключается в следующих двух строках:

gtk_builder_connect_signals (builder, toggle);
gtk_builder_connect_signals (builder, test);

При использовании этой функции user_data, переданная для подключения сигнала, будет применена ко всем обработчикам сигналов.

Если вы выбрали этот путь, то ваши пользовательские данные должны содержать все соответствующие данные, и обработчики должны обращаться к ним соответственно.

Проблема в том, что ваши обработчики сигналов ожидают разных вещей, но вы даете им одни и те же данные.

Из gtk_builder_connect_signals:

User_data

Пользовательские данные для передачи обратно со всеми сигналами

Так что же вы можете сделать?

Если вы хотите сохранить тот же подход, то, например, давайте создадим структуру, содержащую все соответствующие данные, которые затем будут переданы обработчикам обратного вызова / сигнала:

typedef struct {
   interface *toggle;
   test_parameters *test;
} app_user_data;

Затем инициализируйте содержимое:

app_user_data *user_data = g_slice_new (app_user_data);
user_data->toggle = toggle;
user_data->test = test;

Удалите избыточный gtk_builder_connect_signals и замените его нашей новой структурой:

gtk_builder_connect_signals (builder, user_data);

Теперь все обратные вызовы могут использовать необходимые им данные:

G_MODULE_EXPORT gboolean on_eventbox_image_wired_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer toggle) {

   app_user_data *user_data = (app_user_data *) toggle;

    if (((interface *) user_data->toggle)->eth0 == NULL) {
        printf("%s\n", "WIRED: eth0 IS NULL - REMOVE ME");
    }
    ...

G_MODULE_EXPORT gboolean on_eventbox_image_wifi_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer toggle) {
   app_user_data *user_data = (app_user_data *) toggle;

    /* Check to see if we have IP address so we can change the colour otherwise leave it red */
    if (((interface *) user_data->toggle)->wlan0->ip_enabled) {
    ...

G_MODULE_EXPORT gboolean on_switch_test_on_state_set (GtkSwitch *widget, gboolean state, gpointer test) {
   app_user_data *user_data = (app_user_data *) test;

   (((test_parameters *) user_data->test)->active) = state;
   ...

Ваш код с предлагаемыми изменениями должно быть что-то вроде этого:

/*
 *  * STRIPPED DOWN VERSION
 *   */
#include <arpa/inet.h>
#include <gtk/gtk.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


#define IPV4_ENABLED                1
#define IPV4_DISABLED               0
#define SELECTED                    1

typedef struct {
    int ip_enabled;
} net;

typedef struct {
    net *eth0;
    net *wlan0;
    int type;
} interface;

typedef enum {
    IT_ETH0,
    IT_WLAN0,
    IT_UNASSIGNED = 999,
} interface_type;

typedef struct {
    int active;
    interface *port;
    int debug;
} test_parameters;

typedef struct {
   interface *toggle;
   test_parameters *test;
} app_user_data;

void wired (net *eth0);

void wifi (net *wlan0);

int main (int argc, char *argv[]) {

    GtkBuilder *builder;
    GtkWidget *window;
    GError *gtk_error = NULL;

    net *eth0 = g_slice_new (net);
    net *wlan0 = g_slice_new (net);
    interface *toggle = g_slice_new (interface);

    test_parameters *test = g_slice_new (test_parameters);

    test->port->type == IT_UNASSIGNED;

        /*Add interfaces to struct "interface" */
    toggle->eth0 = eth0;
    toggle->wlan0 = wlan0;

    toggle->eth0->ip_enabled = 0;
    toggle->wlan0->ip_enabled = 0;

   app_user_data *user_data = g_slice_new (app_user_data);
   user_data->toggle = toggle;
   user_data->test = test;

    if (NULL == toggle->eth0)
        fprintf (stdout, "%s\n", "eth0 is null ");

    if (NULL == toggle->wlan0)
        fprintf (stdout, "%s\n", "wlan0 is null ");


    gtk_init (&argc, &argv);

    builder = gtk_builder_new ();
    if (!gtk_builder_add_from_file (builder, "glade/window_main.glade", NULL)) {
        fprintf (stdout, "%s%s\n", "Error loading file with the reason: ", gtk_error->message);
        g_free (gtk_error);
        exit (EXIT_FAILURE);
    }

    window = GTK_WIDGET (gtk_builder_get_object (builder, "window_main"));

    gtk_builder_connect_signals (builder, user_data);

    g_object_unref (builder);

    wired (eth0);
    wifi (wlan0);
    gtk_widget_show (window);

    gtk_main ();

    g_slice_free (net, eth0);
    g_slice_free (net, wlan0);
    g_slice_free (interface, toggle);

    return 0;

}

void wifi (net *wlan0) {

    if (1) {
        wlan0->ip_enabled = IPV4_ENABLED;
    }

}

void wired (net *eth0) {

    if (1) {
        eth0->ip_enabled = IPV4_ENABLED;
    }

}

G_MODULE_EXPORT gboolean on_eventbox_image_wired_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer toggle) {

   app_user_data *user_data = (app_user_data *) toggle;

    if (((interface *) user_data->toggle)->eth0 == NULL) {
        printf("%s\n", "WIRED: eth0 IS NULL - REMOVE ME");
    }

    if (((interface *) user_data->toggle) == NULL) {
        printf("%s\n", "WIRED: toggle IS NULL - REMOVE ME");
    }

    if (((interface *) user_data->toggle)->eth0->ip_enabled) {
        printf("%s\n", "WIRED: eth0 is ip enabled - REMOVE ME");
        ((interface *) toggle)->type = IT_ETH0;
        if (((interface *) user_data->toggle)->wlan0->ip_enabled) {
            printf("%s\n", "WIRED: wlan0 is ip enabled, adjust - REMOVE ME");
        }
    }
    return 0;
}

G_MODULE_EXPORT gboolean on_eventbox_image_wifi_button_press_event (GtkWidget *widget, GdkEvent *event, gpointer toggle) {
   app_user_data *user_data = (app_user_data *) toggle;

    /* Check to see if we have IP address so we can change the colour otherwise leave it red */
    if (((interface *) user_data->toggle)->wlan0->ip_enabled) {
        printf("%s\n", "WLAN has IP enabled");
        ((interface *) user_data->toggle)->type = IT_ETH0;
        if (((interface *) user_data->toggle)->eth0->ip_enabled) {
            printf("%s\n", "WLAN: eth0 is ip enabled, adjust - REMOVE ME");
        }
    }

    return 0;       
}

G_MODULE_EXPORT gboolean on_switch_test_on_state_set (GtkSwitch *widget, gboolean state, gpointer test) {
   app_user_data *user_data = (app_user_data *) test;

    (((test_parameters *) user_data->test)->active) = state;

    fprintf (stdout, "%s%i\n", "Signal fired and the state is ", state);

    return 0;

}

void on_window_main_destroy () {

    gtk_main_quit ();
}

Он должен компилироваться, а не segfault, как раньше!