0

I am fairly new to GTK development, not very much aware if GTK supports custom / user-defined events. I am building an application where GUI layout (i.e. the size and position of GtkWidgets) needs to be read from memory and using that data the UI can be created and shown for user interaction.

The GTK Main event loop runs on the Main thread, whereas another (non-main) thread processes the GUI layout and updates the memory. The goal is to send a trigger from the non-main thread to the main thread when the memory is ready for reading. One way this may be achieved is by making the main thread wait in an infinite while loop until the memory is ready with the help of some shared flag. But due to some application architecture specific constraints, we want the main thread to enter the GTK event loop as early as possible.

Therefore, I tried to define and use a Custom GdkEvent (CREATE_WINDOW). In the main thread I have created a GTK_EVENT_BOX (since we don't want any visible window to be created until memory is read) and attached a callback function to it, before it enters the GTK event loop (gtk_main()). The intention is to post a custom event from the non-main thread that would eventually result into the callback function being invoked. The callback function in-turn shall read the memory and create the actual GUI.

The main-thread implementation is as below -

   eventbox = gtk_event_box_new ();

   gtk_event_box_set_above_child (GTK_EVENT_BOX (eventbox), true);

   g_signal_connect (G_OBJECT (eventbox), "event", G_CALLBACK (InternalWindowLifecycleHandler), nullptr);

   gtk_widget_set_realized (eventbox, true);

   gtk_widget_show_all (eventbox);

   gtk_main ();

The non-main thread code is pretty simple (skipped the memory preparation code) -

    GdkEvent *  createwinevent;

    createwinevent = gdk_event_new ((GdkEventType) custEvent::CREATE_WINDOW);

    gdk_event_put (createwinevent);

Note - custEvent is an Enum defined by me.

On contrary to our expectations, posting this custom event does not trigger the callback function InternalWindowLifecycleHandler.

I wonder, is it possible to achieve my goal in GTK, with or without custom / user-defined events?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
saurav
  • 51
  • 3
  • GTK+ is not thread-safe by default. However, it doesn't mean it's impossible but you have to be careful. One example is to use a mutex which grants exclusive access to all the widgets. This mutex must be locked when the main event loop of the GUI thread is entered and may be unlocked e.g. in an event handler to give another thread the opportunity for exclusive access. Of course, it must be locked again before return to main event loop happens. FYI: [GTK3 and multithreading, replacing deprecated functions](https://stackoverflow.com/q/30607429/7478597) – Scheff's Cat Oct 10 '22 at 09:38
  • FYI: [GtK+3.0 multithreaded application](https://stackoverflow.com/q/45202355/7478597) – Scheff's Cat Oct 10 '22 at 09:40
  • @Scheff'sCat - Thanks for the answer and the links. It lead me to the right direction. I could solve my problem using g_idle_add_full(), though I did not have to create a new thread. – saurav Oct 13 '22 at 09:21

1 Answers1

1

The solution to the problem is to use g_idle_add() / g_idle_add_full() in the main thread instead of using GtkEventBox and the callback function.

My problem was once the main thread enters GTK's event loop, the custom event posted by other thread was not being processed as it was not emitting the signal required by GTK to invoke the callback function. However, any function passed to g_idle_add() / g_idle_add_full() is called by the GTK even after entering main event loop if there are no high priority events pending action.

Hence, the modified main-thread code becomes much simpler -

    g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, InternalWindowLifecycleHandler, data, nullptr);

    gtk_main ();

The event posted by the non-main thread could easily be captured and processed inside the InternalWindowLifecycleHandler function -

gboolean
InternalWindowLifecycleHandler (gpointer data)
{
        GdkEvent *      gevent;

    gevent = gdk_event_peek ();

    if (gevent) {

        switch ((int) gevent->type)
        {
        ...
        ...
        }
    }

    return true;
}
saurav
  • 51
  • 3
  • It's a while ago that I used GTK+. However, what I remember is a potential drawback of g_idle_add(): It can produce ventilation noise by keeping the event loop busy all the time (not good while everybody tries to reduce currency consumption to save our environment). A poor work around is to add a sleep into the function (say 50 ms or so). It won't be effective as long as the event loop is filled but will ensure the idle doesn't run too often. – Scheff's Cat Oct 13 '22 at 09:52
  • A better option would be to add the idle event on demand only but then the requirement for thread-syncing might be back. A g_timeout instead of a g_idle with sleep() could be a solution as well. – Scheff's Cat Oct 13 '22 at 09:54
  • Yes, agree on the ventilation noise point. g_timeout_add will surely help to reduce that. – saurav Oct 14 '22 at 17:11