Stefan
Stefan

Reputation: 13

The pointer in signal_connect function is not working correct

I want to use the g_signal_connect() function to change data in a specific struct/class. So, in my opinion, the best way is to use a pointer to the struct. the problem is that the information of the pointer seems to be changing all the time.

I spent a lot of time to figure out why is that happening, but I have no idea. I can compile and run the code without any error but the output is always different.

Later I want to use several event_box to connect to an array of struct or an array of a class (event_box[0] connect to data[0],...).

I hope someone understands what I mean and I would be happy about any help.

#include<gtk/gtk.h>

struct d
{
bool status;
int ID;
};

void end_program(GtkWidget *wid, gpointer ptr)
{
gtk_main_quit();
}

void box_click(GtkWidget *wid, gpointer user_data)
{
    struct d *data = (struct d*)user_data;
    printf("status  = %i\n", data->status);
    printf("ID      = %i\n", data->ID);
}

int main (int argc, char *argv[])
{
    struct d data;
    data.status = 0;
    data.ID = 1;

    gtk_init(&argc, &argv);

    GtkWidget *win = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    GtkWidget *event_box  = gtk_event_box_new();
    g_signal_connect(G_OBJECT(event_box), "button_press_event", G_CALLBACK(box_click), &data); 

    gtk_container_add(GTK_CONTAINER(win), event_box);
    gtk_widget_show_all(win);
    g_signal_connect(win, "delete_event", G_CALLBACK(end_program),NULL);

    gtk_main();

    return 0;
}

Output if I click the box several times:

status  = 4
ID      = 32193184
status  = 5
ID      = 32193184
status  = 4
ID      = 32193184
status  = 6
ID      = 32193184
status  = 4
ID      = 32193184

Upvotes: 1

Views: 619

Answers (2)

Michi
Michi

Reputation: 5307

I will extend a little bit this part where @David C. Rankin said:

(void)wid;      /* cast to void to avoid unused var warning */
(void)event;

Because there is something important which a lot o people does not know about it.

The need of cast is all most all the time wrong used, and this happens because there is another function which is called:

g_signal_connect_swapped()

Which is there to avoid the need of so many casts by swapping the fist parameter with the last one.

Lets take the following example:

#include <gtk/gtk.h>

GtkWidget *createWindow ( const gint width, const gint height );
void user_function      ( GtkWidget *object, gpointer   user_data );

int main ( void )
{
    GtkWidget *window;
    gtk_init ( NULL, NULL );
    /// ***
    window = createWindow( 300, 300 );
    /// ***
    g_signal_connect ( window, "destroy",       G_CALLBACK( user_function ),  NULL );
    /// ***
    gtk_widget_show_all ( window );
    gtk_main();
}

GtkWidget *createWindow ( const gint width, const gint height )
{
    GtkWidget *window = gtk_window_new  ( GTK_WINDOW_TOPLEVEL );
    gtk_widget_set_size_request ( window, width, height );
    gtk_container_set_border_width ( GTK_CONTAINER ( window ), 50 );
    return window;
}

void user_function ( GtkWidget *object, gpointer   user_data )
{
    (void)object;
    (void)user_data;
    g_print( "Goodbye\n" );
    gtk_main_quit();
}

Here as you can see the callback function signature looks like this:

void user_function ( GtkWidget *object, gpointer user_data )

Now there are two casts needed in this case, but we can drop one (gpointer data for example) of them by calling g_signal_connect_swapped like this:

g_signal_connect_swapped ( window, "destroy", G_CALLBACK( user_function ),  NULL 

or even better:

g_signal_connect_swapped ( window, "destroy", G_CALLBACK( user_function ),  (gpointer)"Goodbye" );

Now our program looks like this:

#include <gtk/gtk.h>

GtkWidget *createWindow ( const gint width, const gint height );
void user_function      ( gpointer data );

int main ( void )
{
    GtkWidget *window;
    gtk_init ( NULL, NULL );
    /// ***
    window = createWindow( 300, 300 );
    /// ***
    g_signal_connect_swapped ( window, "destroy", G_CALLBACK( user_function ),  (gpointer)"Goodbye" );
    /// ***
    gtk_widget_show_all ( window );
    gtk_main();
}

GtkWidget *createWindow ( const gint width, const gint height )
{
    GtkWidget *window = gtk_window_new  ( GTK_WINDOW_TOPLEVEL );
    gtk_widget_set_size_request ( window, width, height );
    gtk_container_set_border_width ( GTK_CONTAINER ( window ), 50 );
    return window;
}

void user_function ( gpointer data )
{
    g_print( "%s\n", (char*)data );
    gtk_main_quit();
}

And there are no CASTs anymore.

Here is a better Example:

#include <gtk/gtk.h>

gboolean scroll_callback    ( GtkWidget *widget, GdkEvent  *event );
gboolean show_mouse_pressed ( GtkWidget *widget, GdkEventButton *event );

int main ( void )
{
    GtkWidget *window;
    GtkWidget *button;
    gtk_init( NULL, NULL );
    /// ***
    window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size( GTK_WINDOW( window ), 300, 250 );
    g_signal_connect( window, "destroy", gtk_main_quit, NULL );
    gtk_container_set_border_width( GTK_CONTAINER( window ), 50 );
    /// ***
    button = gtk_button_new_with_mnemonic( "_Click me" );

    gtk_widget_add_events( button, GDK_BUTTON_PRESS );
    gtk_widget_add_events( button, GDK_SCROLL_MASK );

    ///g_signal_connect( button, "clicked", gtk_main_quit, NULL );
    gtk_container_add( GTK_CONTAINER( window ), button );
    /// ***
    g_signal_connect_swapped( button, "button_press_event", G_CALLBACK( show_mouse_pressed ), window );
    g_signal_connect_swapped( button, "scroll_event", G_CALLBACK( scroll_callback ), window );
    /// ***
    gtk_widget_show_all( window );
    gtk_main();
}

gboolean scroll_callback ( GtkWidget *widget, GdkEvent  *event )
{
    if ( event->type == GDK_SCROLL ) /// Scroll was Catched ?
    {
        if ( event->scroll.direction == GDK_SCROLL_DOWN ) /// It is down?
        {
            g_print( "Scroll-Down Detected\n" );
            gtk_window_set_title( GTK_WINDOW( widget ), "Scroll-Down Detected" );
        }

        if ( event->scroll.direction == GDK_SCROLL_UP ) /// It is up?
        {
            g_print( "Scroll-UP Detected\n" );
            gtk_window_set_title( GTK_WINDOW( widget ), "Scroll-UP Detected" );
        }
        return FALSE;
    }
    return TRUE;
}

gboolean show_mouse_pressed ( GtkWidget *widget, GdkEventButton *event )
{
    assert ( widget != NULL );
    if ( gtk_widget_get_has_window ( widget ) )
    {
        if ( event->type == GDK_BUTTON_PRESS )
        {
            g_print( "The mouse clicked the button\n" );
            gtk_window_set_title( GTK_WINDOW( widget ),  "The mouse was Clicked" );
        }
        return TRUE;
    }
    return FALSE;
}

Upvotes: 0

David C. Rankin
David C. Rankin

Reputation: 84632

I hope someone understands what I mean and I would be happy about any help.

Well, yes.. You are using the wrong function prototype for a button-press-event. The prototype for a button-press-event is:

The “button-press-event” signal

gboolean
user_function (GtkWidget *widget,
               GdkEvent  *event,
               gpointer   user_data)

(note: the signal is properly "button-press-event" instead of "button_press_event", though there is a #define allowing the second form to work)

See GtkWidget (Gtk+3 Manual). So what your function should look like is:

gboolean box_click(GtkWidget *wid, GdkEvent *event, gpointer user_data)
{
    struct d *data = user_data;            /* no need for cast, gpointer is void* */
    g_print("status  = %d\n", data->status);
    g_print("ID      = %d\n", data->ID);

    return TRUE;    /* to prevent further handling, FALSE otherwise */

    (void)wid;      /* cast to void to avoid unused var warning */
    (void)event;
}

Additional Nits

Use g_print instead of printf, use gboolean instead of bool. While passing the address of is fine for small structs, for large structs you should allocate with g_slice_new.

Upvotes: 3

Related Questions