GTK3 in C. How to distinguish between a `draw` event generated by my code (gtk_widget_queue_draw) and a `draw` event generated by GTK / expose-event?

In my code, GTK (3, in C) operations are in a single thread.
Every 200ms, my GTK code checks a volatile variable if there is any change (set by another thread), and if yes, queues a redraw

#define REFRESH_MILISECS 200

static gboolean timeout_callback (gpointer data) 
{
    static int last_changements = 0;
    if ( last_changements != changements ) {
        last_changements = changements;

        g_idle_add((GSourceFunc)gtk_widget_queue_draw, (void*)draw_area);

        while (gtk_events_pending()) {
            gtk_main_iteration();
        }
    }
    return 1; // keep going
}

The non-GTK thread simply sets a struct to define the rectangle to be redrawn (accessible via a mutex), and increments the changements volatile int variable.

The drawing function accesses the struct via the mutex and knows what part are to be (re)drawn.

static gboolean draw_status(GtkWidget *widget, cairo_t *cr, void *user_data)

However, and this is the question:

  • How can my callback drawing function know if the drawing event was generated by me (code above), or by Gnome/GTK following a expose-event?

  • since I’m here, another question: since the GTK code is all on its own thread, do I need the g_idle_add wrapping function, and the while (gtk_events_pending()) loop?

Thanks

I do hope this was a simplified version, and you’re not really accessing a variable from multiple threads without acquiring a lock, or using a mutex.

I assume you had to add this because your idle handler was pre-empting a lot of other things, because you didn’t use a lower priority to queue a redraw.

In general, if all you’re doing is queueing a redraw when a thread completes some job, you can avoid all of this, and use:

g_main_context_invoke (g_main_context_default (),
                       (GSourceFunc) gtk_widget_queue_draw,
                       draw_area);

directly from the thread. See: g_main_context_invoke().

It can’t, as there’s no functional difference inside GTK for that. An “expose event” coming from the windowing system is the result of a request for a redraw, so the code is exactly the same in both cases.

Never, ever spin the main loop manually. That API does not even exist in GTK4 any more.

Always use idle or timeout sources to schedule work within the main loop.

If you’re doing something tied to the redraw cycle—for instance, animating something—then use a tick callback tied to a widget.

I don’t see why the R/W of a volatile int variable between 2 threads needs sync, especially in this case.
Even in the (unlikely) case that the R/W would not be atomic, the GTK code only checks for changes of the value every 200ms, and it might as well skip one iteration or do one more.

The whole GTK code runs in a single thread.
Every requested action (button, …) creates a single thread for the task (and only one action runs at one time).

Data exchanges between the 2 threads, GTK and Task (e.g. to refresh status), when an action is performed, is accessed via a mutex.

As a GTK beginner, I searched and read various, sometimes contradictory, information on the Net. In the meantime, since the post was written, this part got simpler, and that extra events loop has been removed. I assumed that since the whole GTK code runs only in a single thread, speed (real time) being not a factor, and (re)drawings are pretty simple, this code should suffice

#define REFRESH_MILISECS 200

static gboolean timer_callback_drawer (gpointer data) 
{
    static int last_changements = 0;

    if ( last_changements != changements || data ) {  // 'data' set if direct call (from GTK code)
        DrawRequest dr;

        if ( data ) {
            dr = *(DrawRequest *)data;
        }
        else {
            last_changements = changements;
            dr = sys_draw_get_request();  // <== via mutex
        }

        if (dr.action == DR_DRAW_SQUARE) {
            int x = BORDER + dr.x0 * CELL_SIZE;
            int y = BORDER + dr.y0 * CELL_SIZE;
            int w = ( dr.x1 - dr.x0 + 1 ) * CELL_SIZE;
            int h = ( dr.y1 - dr.y0 + 1 ) * CELL_SIZE;
            gtk_widget_queue_draw_area(draw_area, x, y, w, h);
        }
        else {
            gtk_widget_queue_draw(draw_area);
        }
    }
    return G_SOURCE_CONTINUE; // keep going
}

Please let me know if your expert eye sees anything alarming from these assumptions and code.
If multiple requests are done on the ‘sys’ side before a redraw, the rectangle is expanded until it is processed. A full redraw supersedes a rectangle request.

Thank you so much for taking the time to answer my question. I really appreciate your help.

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