Search code examples
cmultithreadinguser-interfacegtkgtk2

Update Gtk+2 text view widget from another thread


I am writing a chat client in GTK. The client has the main GTK loop, plus a spawned thread that sits and blocks at the read() function waiting for input from a file descriptor connected to a socket.

Once the read function gets past the blocking, it has a char buffer of text that I would like to append to the GTK Text View widget, however, this is in a thread that is different than the main GTK loop.

How can I most quickly update the GUI from that other thread? In Java, I would have used the SwingUtilities.invokeLater(new Runnable()) method to cause that method to be called from the main thread. I want similar behavior in C and using GTK.

Here is the function that is called from the new thread...

void* messageReceived(void* data)
{
    struct ClientWindow* localVar = (struct ClientWindow*)data;

    while(TRUE)
    {
        char buf[256];
        int bytesRead = read(localVar->socketFileDescriptor, buf, 256);
        GtkTextBuffer* tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(localVar->chatHistoryTextView));

        GtkTextIter end;

        //This code needs to execute in the main thread
        gtk_text_buffer_get_end_iter(tb, &end);
        gtk_text_buffer_insert(tb, &end, buf, -1);

    }
}

Solution

  • The solution I came up with is using the g_idle_add() function. I don't know if I am missing something because the solution is very simple but no one else identified it, so it makes me a bit worried.

    void* messageReceived(void* data)
    {
        struct ClientWindow* localVar = (struct ClientWindow*)data;
        char* message = NULL;
        int bytesRead = 0;
        do
        {
    
            message = bufferedRead(localVar->socketFileDescriptor, 4, &bytesRead);
    
    
            struct UpdateGUIMessage* updateGui = malloc(sizeof(struct UpdateGUIMessage));
            memset(updateGui, 0, sizeof(struct UpdateGUIMessage));
    
            updateGui->clientWindow = localVar;
            updateGui->message = message;
            updateGui->bytesRead = bytesRead;
    
            g_idle_add(G_SOURCE_FUNC(updateGUI), updateGui);
    
        }while(message != NULL);
    }
    
    
    
    bool updateGUI(void* data)
    {
        struct UpdateGUIMessage* localVar = (struct UpdateGUIMessage*)data;
    
        GtkTextBuffer* tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(localVar->clientWindow->chatHistoryTextView));
    
        GtkTextIter end;
    
        gtk_text_buffer_get_end_iter(tb, &end);
        gtk_text_buffer_insert(tb, &end, localVar->message, localVar->bytesRead);
    
        free(localVar->message);
        free(data);
    
        return FALSE;       //So it only gets called once and then is removed
    }