Callback function doesn't modify widget

,

I am trying to write a GTK 4 app in C language. I want to make a callback function that reacts to item chosen from a dropdown widget and updates the entry widget with the value of selected item.
My function works only if I make entry widget a global variable and use it directly in the function. I would prefer to pass a pointer to that widget and make callback function modify it this way. Other functions that were passed pointers to widgets worked, but this one doesn’t want to.

//main.c
#include <gtk/gtk.h>
#include <windows.h>
#include <stdio.h>
#include <stdint.h>

#include "uiElements.h"


static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, void *data[]);
static void activateApp(GApplication *app);


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

  //starting application
  return startApp("exe.GTKforSO", G_APPLICATION_DEFAULT_FLAGS, activateApp, argc, argv);
}

static void activateApp (GApplication *app) {

  static GtkWidget *win;
  static GtkWidget *box;

  //window
  win=initWindow(app, "GTKforSO", 400, 200);

  //box
  box=initBox(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_window_set_child(GTK_WINDOW(win), box);

  static GtkWidget *baudrateDropdown;
  static GtkWidget *baudratesBox;
  static GtkWidget *baudrateEntry;
  static GtkEntryBuffer *baudrateEntryBuffer;


    /** the important part **/
      baudratesBox=initBox(GTK_ORIENTATION_HORIZONTAL, 10);
        gtk_box_append(GTK_BOX(box), baudratesBox);

      char *baudrates[]={"50", "75", "110", "134",
          "150", "200", "300", "600", "1200", "1800",
          "2400", "4800", "9600", "19200", "28800",
          "38400", "57600", "76800", "115200", "230400",
          "460800", "576000", "921600", NULL};
      baudrateDropdown=initDropDown(baudrates, 18); //dropdown widget with 'baudrates' list of items and 18th item selected
        gtk_box_append(GTK_BOX(baudratesBox), baudrateDropdown);

      void *selectedItem=gtk_drop_down_get_selected_item(GTK_DROP_DOWN(baudrateDropdown)); //getting selected item from dropdown widget
      const char *selectedBaudrate=gtk_string_object_get_string(GTK_STRING_OBJECT(selectedItem)); //getting item value as string
      baudrateEntryBuffer=gtk_entry_buffer_new((const char*)selectedBaudrate, strlen(selectedBaudrate)); //new entry buffer widget
      baudrateEntry=gtk_entry_new_with_buffer(GTK_ENTRY_BUFFER(baudrateEntryBuffer)); //new entry widget with buffer 'baudrateEntryBuffer'

      void *entryAndBuffer[2]={baudrateEntry, baudrateEntryBuffer}; //data to pass to callback
      g_signal_connect(baudrateDropdown, "notify::selected-item", G_CALLBACK(baudrateDropdownSelectCallback), entryAndBuffer); //connection to callback
        gtk_box_append(GTK_BOX(baudratesBox), baudrateEntry);
    /**/


  gtk_window_present(GTK_WINDOW(win));
}

static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, void *data[]) {

    void *newlySelectedItem=gtk_drop_down_get_selected_item(GTK_DROP_DOWN(dropdown)); //getting selected item from dropdown widget
    char *selected=gtk_string_object_get_string(GTK_STRING_OBJECT(newlySelectedItem)); //getting item value as string

    GtkEntryBuffer *buffer;
    buffer=gtk_entry_buffer_new(selected, strlen(selected));
    data[1]=buffer; //replacing old buffer with new one

    gtk_entry_set_buffer(GTK_ENTRY(data[0]), data[1]);       //this doesn't work
  //gtk_entry_set_buffer(GTK_ENTRY(baudrateEntry), data[1]); //this works if static GtkWidget *baudrateEntry; is global variable
}
//uiElements.h
#ifndef UI_ELEMENTS_H
#define UI_ELEMENTS_H

#include <gtk/gtk.h>

typedef enum {
    NO_ENTRY=0,
    WITH_ENTRY=1
} withEntry;

int startApp(char *id, GApplicationFlags flags, void (*activateFunc), int argc, char *argv[]);
GtkWidget *initWindow(GApplication  *app, char *windowTitle, int windowWidth, int windowHeight);
GtkWidget *initBox(GtkOrientation orientation, int spacing);
GtkWidget *initDropDown(char *list[], uint8_t selectedByDefault);


#endif /* UI_ELEMENTS_H */
//uiElements.c
#include "uiElements.h"

int startApp(char *id, GApplicationFlags flags, void (*activateFunc), int argc, char *argv[]) {
    GtkApplication *app;
    int stat;

    app=gtk_application_new(id, flags);
    g_signal_connect(app, "activate", G_CALLBACK(activateFunc), NULL);
    stat=g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);

    return stat;
}

GtkWidget *initWindow(GApplication  *app, char *windowTitle, int windowWidth, int windowHeight) {
    GtkWidget *win;
    win=gtk_application_window_new(GTK_APPLICATION(app));
    gtk_window_set_title(GTK_WINDOW(win), windowTitle);
    gtk_window_set_default_size(GTK_WINDOW(win), windowWidth, windowHeight);

    return win;
}

GtkWidget *initBox(GtkOrientation orientation, int spacing) {
    GtkWidget *box;
    box=gtk_box_new(orientation, spacing);
    gtk_box_set_homogeneous(GTK_BOX(box), TRUE);

    return box;
}

GtkWidget *initDropDown(char *list[], uint8_t selectedByDefault) {
    GtkStringList *stringList=gtk_string_list_new((const char * const *)list);
    GtkWidget *dropdown;
    dropdown=gtk_drop_down_new(G_LIST_MODEL(stringList), NULL);
    gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), selectedByDefault);

    return(dropdown);
}

That array ceases to exist when the function returns. You need to allocate something on the heap. Typically you would use a struct here.

Which function do you have on your mind - callback or activateApp?
Also I actually tried to make a struct and pass widgets to callback function via struct. Unfortunately it didn’t work either.

That line is from activateApp(). Array vs struct isn’t the fundamental issue, even if a struct would better-suited for this application. You’re allocating it on the stack, so the pointer you pass to g_signal_connect() becomes invalid as soon as the function returns.

You should explicitly allocate a struct on the heap using malloc() or preferably g_new0().

I defined a structure with all widgets and used g_new0 to initialise it in main function. I added used widgets to this structure and passed whole structure to callback function. Unfortunately program crashes when I select an item from a dropdown list.

Whole code:

//main.c
#include <gtk/gtk.h>
#include <windows.h>
#include <stdio.h>
#include <stdint.h>

#include "uiElements.h"


typedef struct {

   GtkWidget *win;
   GtkWidget *box;
   GtkWidget *baudrateDropdown;
   GtkWidget *baudratesBox;
   GtkWidget *baudrateEntry;
   GtkEntryBuffer *baudrateEntryBuffer;

} WidgetStruct;

WidgetStruct widgetStr;
WidgetStruct *widgetStrPtr=&widgetStr;

static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, WidgetStruct *data);
static void activateApp(GApplication *app);


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

  widgetStrPtr=g_new0(WidgetStruct, 1);

  //starting application
  return startApp("exe.GTKforSO", G_APPLICATION_DEFAULT_FLAGS, activateApp, argc, argv);
}

static void activateApp (GApplication *app) {

  static GtkWidget *win;
  static GtkWidget *box;

  //window
  win=initWindow(app, "GTKforSO", 400, 200);

  //box
  box=initBox(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_window_set_child(GTK_WINDOW(win), box);

  static GtkWidget *baudrateDropdown;
  static GtkWidget *baudratesBox;
  static GtkWidget *baudrateEntry;
  static GtkEntryBuffer *baudrateEntryBuffer;


    /** the important part **/
      baudratesBox=initBox(GTK_ORIENTATION_HORIZONTAL, 10);
        gtk_box_append(GTK_BOX(box), baudratesBox);

      char *baudrates[]={"50", "75", "110", "134",
          "150", "200", "300", "600", "1200", "1800",
          "2400", "4800", "9600", "19200", "28800",
          "38400", "57600", "76800", "115200", "230400",
          "460800", "576000", "921600", NULL};
      baudrateDropdown=initDropDown(baudrates, 18); //dropdown widget with 'baudrates' list of items and 18th item selected
        gtk_box_append(GTK_BOX(baudratesBox), baudrateDropdown);

      void *selectedItem=gtk_drop_down_get_selected_item(GTK_DROP_DOWN(baudrateDropdown)); //getting selected item from dropdown widget
      const char *selectedBaudrate=gtk_string_object_get_string(GTK_STRING_OBJECT(selectedItem)); //getting item value as string
      baudrateEntryBuffer=gtk_entry_buffer_new((const char*)selectedBaudrate, strlen(selectedBaudrate)); //new entry buffer widget
      baudrateEntry=gtk_entry_new_with_buffer(GTK_ENTRY_BUFFER(baudrateEntryBuffer)); //new entry widget with buffer 'baudrateEntryBuffer'

    widgetStrPtr->box=box;
    widgetStrPtr->baudrateDropdown=baudrateDropdown;
    widgetStrPtr->baudratesBox=baudratesBox;
    widgetStrPtr->baudrateEntry=baudrateEntry;
    widgetStrPtr->baudrateEntryBuffer=baudrateEntryBuffer;

      g_signal_connect(baudrateDropdown, "notify::selected-item", G_CALLBACK(baudrateDropdownSelectCallback), widgetStrPtr); //connection to callback
        gtk_box_append(GTK_BOX(baudratesBox), baudrateEntry);
    /**/


  gtk_window_present(GTK_WINDOW(win));
}

static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, WidgetStruct *data) {

    WidgetStruct *_widgetStr=data;

    void *newlySelectedItem=gtk_drop_down_get_selected_item(GTK_DROP_DOWN(dropdown)); //getting selected item from dropdown widget

    const char *selected=gtk_string_object_get_string(GTK_STRING_OBJECT(newlySelectedItem)); //getting item value as string

    gtk_editable_set_text(GTK_EDITABLE(_widgetStr->baudrateEntry), selected);
}

supposedly

- static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, WidgetStruct *data);
+ static void baudrateDropdownSelectCallback(GtkDropDown *dropdown, void *unused, WidgetStruct *data);
1 Like

This change fixed the program and now function works properly, without crashing the program. Thank you!
Could you explain how does this work? I don’t understand the magic behind this code.

Idk, where have you found that function declaration that corrupt the stack?
if I’m not mistaken, in source it looks like gtk/gtkdropdown.c · main · GNOME / gtk · GitLab

1 Like

I tried making a callback function like for buttons, and the buttons callback functions I used looked like in this code from ToshioCP tutorial:

#include <gtk/gtk.h>

static void
click_cb (GtkButton *btn, GtkWindow *win) {
  gtk_window_destroy (win);
}

static void
app_activate (GApplication *app) {
  GtkWidget *win;
  GtkWidget *btn;

  win = gtk_application_window_new (GTK_APPLICATION (app));
  gtk_window_set_title (GTK_WINDOW (win), "lb3");
  gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);

  btn = gtk_button_new_with_label ("Close");
  gtk_window_set_child (GTK_WINDOW (win), btn);
  g_signal_connect (btn, "clicked", G_CALLBACK (click_cb), win);

  gtk_window_present (GTK_WINDOW (win));
}

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

  app = gtk_application_new ("com.github.ToshioCP.lb3", G_APPLICATION_DEFAULT_FLAGS);
  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;
}

All previous callback functions I made followed this template and worked. For example this one also has another parameter:

static void portRefreshButtonCallback(GtkButton *button, GtkDropDown *portsDropdown) {
  findCommunicationPorts();

  GtkStringList *newList=gtk_string_list_new((const char * const *)portNames);
  GListModel *model = G_LIST_MODEL(newList);
  gtk_drop_down_set_model(GTK_DROP_DOWN(portsDropdown), G_LIST_MODEL(model));

  if (portNames[0]==NULL) {
    char *problem[2]={"Problem with detecting ports.",NULL};
    GtkStringList *newList=gtk_string_list_new((const char * const *)problem);
    GListModel *model = G_LIST_MODEL(newList);
    gtk_drop_down_set_model(GTK_DROP_DOWN(portsDropdown), G_LIST_MODEL(model));
  }

}

It may be that my documentation searching skills are poor and that’s why I made this mistake, but honestly I am not sure how I should have approached this problem.

I hope I understood your question correctly. If not, let me know.

Documentation says that “Handlers are documented as having type GCallback, but this is simply a generic placeholder type — an artifact of the GSignal APIs being polymorphic.”
I.e. signal handler for “clicked” has own arguments, “notify::selected-item” has another set and number of arguments, and so on. It’s how I understand it works.

1 Like

That would make sense. Also your explanation is much clearer than the official one from documentation.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.