Rendering in a worker thread



Hi,

I'm new to GTK. I want to do some complex rendering in a worker thread. I've done a basic sample but I've got a problem on the refresh of the  window.
The rendering thread draws and fills a polygon in a pixmap, then copies the pixmap content into a drawing area. After a complete loop, the rendering thread sleeps during 1 second and do a new loop. At each loop, the color used to fill the polygon is changed.
At the first time, the polygon is drawn in red. In the next loop, the color used is blue and in the next loop, red color is used again, and so one.
The problem I've in my sample is that in the first rendering loop, the polygon is not always drawn in red.
I suppose I've not correctly understand the way to require a refresh when the rendering is done in a worker thread and not from the main thread.

Here is my code sample :

#include <string.h>
#include <unistd.h>
#include <gtk/gtk.h>

// Handlers
static gboolean s_boDestroy();
static gboolean s_boConfigureEvent(GtkWidget* pWidget, GdkEventConfigure* pEvent);
static gboolean s_boExposeEvent(GtkWidget* pArea, GdkEventExpose* pEvent);

// Thread
static void* s_pRendererThread(void* pArgs);

typedef struct
{
    GtkWidget* pWidget;
    GdkPixmap* pPixmap;
    int nWidth;
    int nHeight;
} ThreadArgs;

// Static Variables
static GdkPixmap* s_pPixmap = NULL;
static gboolean s_boInitialConfig = false;
static ThreadArgs s_ThreadArgs;


int main(int argc, char** argv)
{
    // Init
    memset(&s_ThreadArgs, 0, sizeof(ThreadArgs));
    s_ThreadArgs.nWidth = 240;
    s_ThreadArgs.nHeight = 320;
   
    // Init threads   
    g_thread_init(NULL);
    gdk_threads_init();
   
    // Init GTK
    gtk_init(&argc, &argv);
   
    // Main Window
    GtkWidget* pMainWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    g_signal_connect(G_OBJECT(pMainWindow), "destroy", G_CALLBACK(s_boDestroy), NULL);
    gtk_window_set_title(GTK_WINDOW(pMainWindow), "renderer_thread");
    gtk_widget_set_size_request(pMainWindow, 240, 320);
       
    // Drawing Area
    GtkWidget* pDrawingArea = gtk_drawing_area_new();
    gtk_container_add(GTK_CONTAINER(pMainWindow), pDrawingArea);   
    g_signal_connect(G_OBJECT(pDrawingArea), "configure_event", G_CALLBACK(s_boConfigureEvent), NULL);
    g_signal_connect(G_OBJECT(pDrawingArea), "expose_event", G_CALLBACK(s_boExposeEvent), NULL);
   
    // Display the widgets
    gtk_widget_show_all(pMainWindow);
   
    // Enter main loop
    gdk_threads_enter();
    gtk_main();
    gdk_threads_leave();
   
    return 0;
}

static gboolean s_boDestroy()
{
    gtk_main_quit();
    return TRUE;
}

static gboolean s_boConfigureEvent(GtkWidget* pWidget, GdkEventConfigure* pEvent)
{
    if(s_boInitialConfig == false)
    {
        s_boInitialConfig = true;
       
        int nWidth = 240;
        int nHeight = 320;
       
        // Create the Pixmap
        s_pPixmap = gdk_pixmap_new(pWidget->window, nWidth, nHeight, -1);
       
        // Update Thread Args
        s_ThreadArgs.nWidth = nWidth;
        s_ThreadArgs.nHeight = nHeight;   
        s_ThreadArgs.pWidget = pWidget;
        s_ThreadArgs.pPixmap = s_pPixmap;       
       
        // Clear pixmap with white brush
        gboolean boFilled = TRUE;
        gdk_draw_rectangle(
            s_pPixmap,
            pWidget->style->white_gc,
            boFilled,
            0, 0,
            pWidget->allocation.width, pWidget->allocation.height
        );
       
        // Create the thread
        GError* pError = NULL;
        g_thread_create(s_pRendererThread, &s_ThreadArgs, FALSE, &pError);
    }   
   
    return TRUE;
}

static gboolean s_boExposeEvent(GtkWidget* pWidget, GdkEventExpose* pEvent)
{
    return TRUE;
}

static void* s_pRendererThread(void* pArgs)
{
    ThreadArgs* pThreadArgs = static_cast<ThreadArgs*>(pArgs);
   
    int nXMin = 5;
    int nXMax = pThreadArgs->nWidth-nXMin;
    int nYMin = 5;
    int nYMax = pThreadArgs->nHeight-nYMin;

    GdkPoint Points[4];
    Points[0].x = nXMin; Points[0].y = nYMin;
    Points[1].x = nXMax; Points[1].y = nYMin;
    Points[2].x = nXMax; Points[2].y = nYMax;
    Points[3].x = nXMin; Points[3].y = nYMax;

   
    GdkColor RedColor, BlueColor;
    memset(&RedColor, 0, sizeof(GdkColor));
    RedColor.red = 0xFFFF;
   
    memset(&BlueColor, 0, sizeof(GdkColor));
    BlueColor.blue = 0xFFFF;
   
    int nStep = 0;
    gboolean boFilled = TRUE;
    GdkColor* pCurrentColor = &RedColor;
   
    GtkWidget* pWidget = pThreadArgs->pWidget;
    GdkPixmap* pPixmap = pThreadArgs->pPixmap;
    GdkGC* pGC = gdk_gc_new(pPixmap);
   
    for(;;)
    {
        gdk_threads_enter();
       
        // Select current color (Red or Blue)
        pCurrentColor = ((nStep++%2)==0 ? &RedColor : &BlueColor);
        gdk_gc_set_rgb_fg_color(pGC, pCurrentColor);
       
        // Draw polygon onto pixmap
        gdk_draw_polygon(pPixmap, pGC, boFilled, Points, 4);
       
        // Copy pixmap onto widget
        gdk_draw_drawable(
            pWidget->window,
            pWidget->style->fg_gc[GTK_WIDGET_STATE(pWidget)],
            pPixmap,
            0, 0,
            0, 0,
            -1, -1
        );       
       
        gdk_flush();
       
        gdk_threads_leave();
       
        sleep(1);
    }
   
    return NULL;
}



Thanks for any help.

Regards,
Marc Deldem





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]