Search code examples
cgcccallbackgtkgtk4

Passing callback arguments through GTK's g_signal_connect() leads to unexpected values in C


I am working on one small project in C using GTK4 and Miniaudio. I need to pass some arguments through g_signal_connect() to several widgets. Since I am new to GTK, I tried to experiment on a basic GTK example (hello-world-gtk example) on GTK's Getting started. But, when I pass any callback argument through g_signal_connect() and I get unexpected values.

Here is the example of modified hello-world-gtk code:

#include <stdio.h>
#include <gtk/gtk.h>

void hello(GtkWidget *widget, void *data) {
  printf("pointer in hello(): %p\n", data);
  printf("data value: %d\n", *(int*)data);
}

void activate(GtkApplication *app, gpointer user_data) {
  GtkWidget *window;
  GtkWidget *button;

  int abc = 5;
  void *ptr = &abc;

  window = gtk_application_window_new(app);
  gtk_window_set_title(GTK_WINDOW(window), "Window");
  gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

  button = gtk_button_new_with_label("Hello World");

  printf("pointer in activate(): %p\n", ptr);
  printf("abc value: %d\n", *(int*)ptr);
  // Problem occurs here.
  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(hello), ptr);
  gtk_window_set_child(GTK_WINDOW(window), button);

  gtk_window_present(GTK_WINDOW(window));
}

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

  app = gtk_application_new("org.gtk.example", G_APPLICATION_REPLACE);
  g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
  status = g_application_run(G_APPLICATION(app), argc, argv);
  g_object_unref(app);

  return status;
}

It is compiled with gcc $(pkg-config --cflags gtk4) -o hello-world-gtk hello-world-gtk.c $(pkg-config --libs gtk4) command.

Terminal output is:

pointer in activate(): 0x7fff5f4c8124
abc value: 5
pointer in hello(): 0x7fff5f4c8124
data value: 0
pointer in hello(): 0x7fff5f4c8124
data value: 0

On initialization first two lines in terminal are printed and on every button click new 2 lines are printed out (the "pointer in hello():..." and "data value:...").

I expected, as probably guessed, passed pointer's value to be printed, not 0 everytime, or:

pointer in activate(): 0x7fff5f4c8124
abc value: 5
pointer in hello(): 0x7fff5f4c8124
data value: 5
pointer in hello(): 0x7fff5f4c8124
data value: 5

I have tried googling how others use the function g_signal_connect(), syntax is very similar, but they get expected values (like here, created my small code demo to see if I understant void* and callbacks in C, and yet I get expected values in that demo.

The demo:

#include <stdio.h>

void doing_things(int (*fx)(int), void *param) {
  int x = *(int*)param;
  printf("Applied function: %d\n", fx(x));
}


int square(int value) {
  return value * value;
}

int main() {
  int my_val = 5;
  int (*my_square)(int) = square;
  void *my_pointer = &my_val;
  doing_things(my_square, my_pointer);
  return 0;
}

Demo's output:

Applied function: 25

I've tried changing types of data and typecasting to different types (gpointer is basically void*), and no success.

I have an idea to make callback parameters extern, but it is considered a back practice how much I know.

If you know how to fix this problem, I would really appreciate it!

Thanks in advance.


Solution

  • int abc = 5;
    

    This variable is local to the activate() function and the pointer you provide to g_signal_connect ...

    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(hello), ptr);
    

    ... will be dangling as soon as activate() returns. Dereferencing the pointer inside hello() therefore has undefined behavior.

    A simple fix would be to make it static to make the variable survive until the end of program execution:

    static int abc = 5;