G_cclosure_new_swap does not set the free_data function

I am trying to create a button which uses GtkAccelLabel and at the time where the user press CTRL + Q both callbacks ( first_clbk and second_clbk ) should be called.

The program works fine, but the msg created with g_strdup does not get destroyed.
This happens because the function free_data never gets called.

Here is a demo program:

#include <gtk/gtk.h>

static void free_data ( gchar *data, G_GNUC_UNUSED GClosure *clo )
{
    g_print ( "The data memory was cleared\n" );
    g_free ( data );
}

static void first_clbk ( const gchar *const msg )
{
    g_print ( "%s\n", msg );
}

static void second_clbk ( gchar *msg )
{
    g_print ( "\t%s\n", msg );
}

int main ( void )
{
    GtkWidget *window;
    GtkWidget *accelLabel;
    GtkAccelGroup *accel_group;
    GtkWidget *button;

    /// ***
    gtk_init ( NULL, NULL );

    /// *** Create a new Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 400, 200 );
    g_signal_connect ( window, "delete-event", gtk_main_quit, NULL );
    g_signal_connect ( window, "destroy",      gtk_main_quit, NULL );

    /// ***
    accel_group = gtk_accel_group_new ();
    gtk_window_add_accel_group ( GTK_WINDOW ( window ), accel_group );

    /// ***
    button = gtk_button_new( );
    g_object_set ( button, "margin", 100, NULL );
    gtk_container_add ( GTK_CONTAINER ( window ), button );
    g_signal_connect_swapped ( button, "clicked", G_CALLBACK ( first_clbk ), ( gpointer ) "You clicked the button" );

    /// ***
    accelLabel = gtk_accel_label_new ( "Quit" );
    gtk_container_add ( GTK_CONTAINER ( button ), accelLabel );
    gtk_accel_label_set_accel ( GTK_ACCEL_LABEL ( accelLabel ), GDK_KEY_q, GDK_CONTROL_MASK );
    gtk_accel_label_set_accel_widget (  GTK_ACCEL_LABEL ( accelLabel ), button );

    /// *** Create the msg with g_strdup
    gchar *msg;
    msg = g_strdup ( "You pressed CTRL + Q" );

    /// *** create the closure callback
    GClosure *closure_clbk;
    closure_clbk = g_cclosure_new_swap ( G_CALLBACK ( second_clbk ), msg, ( GClosureNotify ) free_data );

    /// *** connect the callback with the accel_group
    gtk_accel_group_connect ( accel_group, GDK_KEY_q, GDK_CONTROL_MASK, 0, closure_clbk );

    /// *** Set the accel closure
    gtk_accel_label_set_accel_closure ( GTK_ACCEL_LABEL ( accelLabel ), closure_clbk );

    /// *** add the accelerator
    gtk_widget_add_accelerator ( button, "activate", accel_group, GDK_KEY_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE );

    /// ***
    gtk_widget_show_all ( window );
    gtk_main ();
}

Why free_data never gets called?

You should do the following to indicate that the closure is of no use anymore.

gtk_accel_group_disconnect (accel_group, closure_clbk);

1 Like

Where should this call take place, in free_data?

No.

#include <gtk/gtk.h>

GClosure *closure_clbk;
GtkAccelGroup *accel_group;

static void free_data ( gchar *data, G_GNUC_UNUSED GClosure *clo )
{
    g_print ( "The data memory was cleared\n" );
    g_free ( data );
}

static void first_clbk ( const gchar *const msg )
{
    g_print ( "%s\n", msg );
}

static void second_clbk ( gchar *msg )
{
    g_print ( "\t%s\n", msg );
}

int start_ui ( void )
{
    GtkWidget *window;
    GtkWidget *accelLabel;
    GtkWidget *button;

    /// ***
    gtk_init ( NULL, NULL );

    /// *** Create a new Window
    window = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
    gtk_window_set_default_size ( GTK_WINDOW ( window ), 400, 200 );
    g_signal_connect ( window, "delete-event", gtk_main_quit, NULL );
    g_signal_connect ( window, "destroy",      gtk_main_quit, NULL );

    /// ***
    accel_group = gtk_accel_group_new ();
    gtk_window_add_accel_group ( GTK_WINDOW ( window ), accel_group );

    /// ***
    button = gtk_button_new( );
    g_object_set ( button, "margin", 100, NULL );
    gtk_container_add ( GTK_CONTAINER ( window ), button );
    g_signal_connect_swapped ( button, "clicked", G_CALLBACK ( first_clbk ), ( gpointer ) "You clicked the button" );

    /// ***
    accelLabel = gtk_accel_label_new ( "Quit" );
    gtk_container_add ( GTK_CONTAINER ( button ), accelLabel );
    gtk_accel_label_set_accel ( GTK_ACCEL_LABEL ( accelLabel ), GDK_KEY_q, GDK_CONTROL_MASK );
    gtk_accel_label_set_accel_widget (  GTK_ACCEL_LABEL ( accelLabel ), button );

    /// *** Create the msg with g_strdup
    gchar *msg;
    msg = g_strdup ( "You pressed CTRL + Q" );

    /// *** create the closure callback
    closure_clbk = g_cclosure_new_swap ( G_CALLBACK ( second_clbk ), msg, ( GClosureNotify ) free_data );

    /// *** connect the callback with the accel_group
    gtk_accel_group_connect ( accel_group, GDK_KEY_q, GDK_CONTROL_MASK, 0, closure_clbk );

    /// *** Set the accel closure
    gtk_accel_label_set_accel_closure ( GTK_ACCEL_LABEL ( accelLabel ), closure_clbk );

    /// *** add the accelerator
    gtk_widget_add_accelerator ( button, "activate", accel_group, GDK_KEY_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE );

    /// ***
    gtk_widget_show_all ( window );
    gtk_main ();
}

int main( void )
{
    gboolean ret;

    start_ui();
    ret = gtk_accel_group_disconnect (accel_group, closure_clbk);
    g_assert (ret);

    return 0;
}

Well after reading the documentation again I managed to understand it and indeed I was supposed to make that call.

Thank you for your help.

1 Like