
/*
 * Simple thread-guard demonstration.
 */

#include <gtk/gtk.h>
#include "thread-guard.h"

typedef struct {
    GtkProgressBar *pb;
    gdouble progress;
} HelperStruct;

gpointer helper(HelperStruct *info)
{
    /* This function is being executed in the main thread, so we can safely do
     * any GTK+ stuff here.
     */

    gdouble *oldval = g_malloc(sizeof(gdouble));
    *oldval = gtk_progress_bar_get_fraction(info->pb);

    gtk_progress_bar_set_fraction(info->pb, info->progress);

    return oldval;
}

gpointer thread(GtkProgressBar *pb)
{
    // Insert something really complicated here.
    g_usleep(3000000); // sleep for 3s

#if 0
    // If your app was intended only to run on Linux, you'd just do some kind of
    // the following:

    gdk_threads_enter();
    gtk_progress_bar_set_fraction(pb, 0.5);
    gdk_threads_leave();

    // But on Win32, under some certain circumstances really weird things
    // happened. So you decided to use my thread-guard code.
#endif


    /* Method 1 (the easy way out):
     * The function is not of type »gpointer func(gpointer)«. We use a helper
     * function that IS of this type.
     */
    HelperStruct *info = g_malloc(sizeof(HelperStruct));
    info->pb = pb;
    info->progress = 0.5;
    // Call our helper func in the main thread and keep the return value.
    gdouble *oldval = guarded_call((GuardedCallback)helper, info);
    g_debug("Info: Set progress from %f to %f", *oldval, 0.5);
    g_free(oldval);


    g_usleep(3000000); // sleep for 3s once more


    /* Method 2 (things get more complicated):
     * The function is not of type »gpointer func(gpointer)«. We do not want to
     * use a helper func, or just implement it cleanly. To simplify things, we
     * only fetch the current progress instead of getting it as well.
     */
    // Create 2 GValues: one holds the GtkProgressBar argument, the other our
    // current progress as a gdouble.
    GValue arg[2];
    // We initialize the GValues according to the function's prototype:
    // void gtk_progress_bar_set_fraction(GtkProgressBar, gdouble)
    // ... after first clearing them.
    // Note: You could as well have dynamically allocated them via g_malloc0.
    memset(arg, 0, sizeof(GValue) * 2);

    g_value_init(&(arg[0]), GTK_TYPE_PROGRESS_BAR);
    g_value_init(&(arg[1]), G_TYPE_DOUBLE);

    // We set the »arg« GValue to the progress bar object which we want to pass
    // to the function.
    g_value_set_object(&(arg[0]), pb);
    // Analogous.
    g_value_set_double(&(arg[1]), 1.0);

    // We create a GClosure for the function and set an according marshal (in
    // this case, one that handles the prototype »void func(GtkProgressBar, gdouble)«,
    // »void func(GObject, gdouble)« or »void func(gpointer, gdouble)«. We can
    // ignore the user_data argument.
    GClosure *closure = g_cclosure_new(G_CALLBACK(gtk_progress_bar_set_fraction), NULL, NULL);
    g_closure_set_marshal(closure, g_cclosure_marshal_VOID__DOUBLE);

    // Now, we actually invoke the closure...
    guarded_invoke(closure, NULL, 2, arg, NULL);
    g_debug("Info: Set progress to %f", 1.0);
    // ... and free it.
    g_closure_unref(closure);

    
    g_usleep(3000000); // last time :-)


    /* Method 3 (don't worry, be happy)
     * The function is of type »gpointer func(gpointer)« or something
     * compatible.
     */
    guarded_call((GuardedCallback)gtk_main_quit, NULL);

    return NULL;
}

int main(int argc, char **argv)
{
    // General initialisation.
    g_thread_init(NULL);
    gtk_init(&argc, &argv);
    guard_init();

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 25);
    gtk_widget_show(window);

    GtkWidget *pb = gtk_progress_bar_new();
    gtk_container_add(GTK_CONTAINER(window), pb);
    gtk_widget_show(pb);

    // Spawn thread.
    g_thread_create((GThreadFunc)thread, GTK_PROGRESS_BAR(pb), FALSE, NULL);

    gtk_main();

    return 0;
}

