[Rawstudio-commit] r2221 - trunk/librawstudio
Anders Brander
anders at brander.dk
Tue Feb 24 03:58:03 CET 2009
Author: abrander
Date: 2009-02-24 03:58:03 +0100 (Tue, 24 Feb 2009)
New Revision: 2221
Added:
trunk/librawstudio/rs-job-queue.c
trunk/librawstudio/rs-job-queue.h
Modified:
trunk/librawstudio/Makefile.am
trunk/librawstudio/rawstudio.h
Log:
Added RSJobQueue.
Modified: trunk/librawstudio/Makefile.am
===================================================================
--- trunk/librawstudio/Makefile.am 2009-02-23 00:22:15 UTC (rev 2220)
+++ trunk/librawstudio/Makefile.am 2009-02-24 02:58:03 UTC (rev 2221)
@@ -18,6 +18,7 @@
rs-filter.h \
rs-output.h \
rs-plugin-manager.h \
+ rs-job-queue.h \
rs-utils.h \
rs-math.h \
rs-settings.h \
@@ -39,6 +40,7 @@
rs-filter.c rs-filter.h \
rs-output.c rs-output.h \
rs-plugin-manager.c rs-plugin-manager.h \
+ rs-job-queue.c rs-job-queue.h \
rs-utils.c rs-utils.h \
rs-math.c rs-math.h \
rs-settings.c rs-settings.h \
Modified: trunk/librawstudio/rawstudio.h
===================================================================
--- trunk/librawstudio/rawstudio.h 2009-02-23 00:22:15 UTC (rev 2220)
+++ trunk/librawstudio/rawstudio.h 2009-02-24 02:58:03 UTC (rev 2221)
@@ -39,6 +39,7 @@
#include "rs-filter.h"
#include "rs-output.h"
#include "rs-plugin-manager.h"
+#include "rs-job-queue.h"
#include "rs-utils.h"
#include "rs-settings.h"
#include "rs-adobe-coeff.h"
Added: trunk/librawstudio/rs-job-queue.c
===================================================================
--- trunk/librawstudio/rs-job-queue.c (rev 0)
+++ trunk/librawstudio/rs-job-queue.c 2009-02-24 02:58:03 UTC (rev 2221)
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2006-2009 Anders Brander <anders at brander.dk> and
+ * Anders Kvist <akv at lnxbx.dk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include <rawstudio.h>
+#include "rs-job-queue.h"
+
+struct _RSJobQueueSlot {
+ GtkWidget *container;
+ GtkWidget *label;
+ GtkWidget *progress;
+};
+
+struct _RSJob {
+ RSJobFunc func;
+ RSJobQueue *job_queue;
+ RSJobQueueSlot *slot;
+ gpointer data;
+ gpointer result;
+ gboolean done;
+ GCond *done_cond;
+ GMutex *done_mutex;
+};
+
+struct _RSJobQueue {
+ GObject parent;
+ gboolean dispose_has_run;
+
+ GMutex *lock;
+ GThreadPool *pool;
+ gint n_slots;
+ GtkWidget *window;
+ GtkWidget *box;
+};
+
+G_DEFINE_TYPE (RSJobQueue, rs_job_queue, G_TYPE_OBJECT)
+
+static void job_consumer(gpointer data, gpointer unused);
+
+static void
+rs_job_queue_dispose (GObject *object)
+{
+ RSJobQueue *job_queue = RS_JOB_QUEUE(object);
+
+ if (!job_queue->dispose_has_run)
+ {
+ job_queue->dispose_has_run = TRUE;
+
+ g_mutex_free(job_queue->lock);
+ }
+
+ /* Chain up */
+ G_OBJECT_CLASS(rs_job_queue_parent_class)->dispose(object);
+}
+
+static void
+rs_job_queue_class_init (RSJobQueueClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = rs_job_queue_dispose;
+}
+
+static void
+rs_job_queue_init(RSJobQueue *job_queue)
+{
+ job_queue->dispose_has_run = FALSE;
+ job_queue->lock = g_mutex_new();
+ job_queue->pool = g_thread_pool_new(((GFunc) job_consumer), NULL, rs_get_number_of_processor_cores(), TRUE, NULL);
+ job_queue->n_slots = 0;
+ job_queue->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ job_queue->box = gtk_vbox_new(TRUE, 1);
+
+ gtk_container_add(GTK_CONTAINER(job_queue->window), job_queue->box);
+
+ gtk_window_set_deletable(GTK_WINDOW(job_queue->window), FALSE);
+ gtk_window_set_accept_focus(GTK_WINDOW(job_queue->window), FALSE);
+ gtk_window_set_keep_above(GTK_WINDOW(job_queue->window), TRUE);
+ gtk_window_set_skip_pager_hint(GTK_WINDOW(job_queue->window), TRUE);
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(job_queue->window), TRUE);
+ gtk_window_set_title(GTK_WINDOW(job_queue->window), "");
+ gtk_window_set_type_hint(GTK_WINDOW(job_queue->window), GDK_WINDOW_TYPE_HINT_UTILITY);
+
+ /* Let's spice it up a notch! :) */
+#if GTK_CHECK_VERSION(2,12,0)
+ gtk_window_set_opacity(GTK_WINDOW(job_queue->window), 0.85);
+#endif
+
+ /* Set the gravity, so that resizes will still result in a window
+ * positioned in the lower left */
+ gtk_window_set_gravity(GTK_WINDOW(job_queue->window), GDK_GRAVITY_SOUTH_EAST);
+
+ /* Place the window in lower left corner of screen */
+ gtk_window_move(GTK_WINDOW(job_queue->window), 0, gdk_screen_get_height(gdk_display_get_default_screen(gdk_display_get_default())));
+}
+
+/**
+ * Get a new RSJobQueue
+ * @return A new RSJobQueue
+ */
+static RSJobQueue *
+rs_job_queue_new(void)
+{
+ return g_object_new (RS_TYPE_JOB_QUEUE, NULL);
+}
+
+/**
+ * Return the RSJobQueue singleton
+ * @note THis function should be thread safe
+ * @return A RSJobQueue singleton
+ */
+static RSJobQueue *
+rs_job_queue_get_singleton()
+{
+ static RSJobQueue *singleton = NULL;
+ GStaticMutex lock = G_STATIC_MUTEX_INIT;
+
+ g_static_mutex_lock(&lock);
+ if (!singleton)
+ singleton = rs_job_queue_new();
+ g_static_mutex_unlock(&lock);
+
+ g_assert(RS_IS_JOB_QUEUE(singleton));
+
+ return singleton;
+}
+
+/**
+ * Add a new processing slot to a RSJobQueue window
+ * @param job_queue A RSJobQueue
+ * @return A new RSJobQueueSlot
+ */
+static RSJobQueueSlot *
+rs_job_queue_add_slot(RSJobQueue *job_queue)
+{
+ RSJobQueueSlot *slot = g_new0(RSJobQueueSlot, 1);
+
+ g_mutex_lock(job_queue->lock);
+ gdk_threads_enter();
+
+ slot->container = gtk_vbox_new(FALSE, 0);
+ slot->progress = gtk_progress_bar_new();
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), "Hello...");
+ gtk_box_pack_start(GTK_BOX(slot->container), slot->progress, FALSE, TRUE, 1);
+
+ gtk_box_pack_start(GTK_BOX(job_queue->box), slot->container, FALSE, TRUE, 1);
+
+ /* If we previously got 0 slots open, position the window again */
+ if (job_queue->n_slots == 0)
+ gtk_window_move(GTK_WINDOW(job_queue->window), 0, gdk_screen_get_height(gdk_display_get_default_screen(gdk_display_get_default())));
+
+ /* For some reason this must be called everytime to trigger correct placement?! */
+ gtk_widget_show_all(job_queue->window);
+
+ job_queue->n_slots++;
+
+ gdk_threads_leave();
+ g_mutex_unlock(job_queue->lock);
+
+ return slot;
+}
+
+/**
+ * Remove and frees a RSJobQueueSlot from a RSJobQueue window
+ * @param job_queue A RSJobQueue
+ * @param slot The slot to remove and free
+ */
+static void
+rs_job_queue_remove_slot(RSJobQueue *job_queue, RSJobQueueSlot *slot)
+{
+ g_mutex_lock(job_queue->lock);
+ gdk_threads_enter();
+
+ gtk_container_remove(GTK_CONTAINER(job_queue->box), slot->container);
+ job_queue->n_slots--;
+
+ /* If we got less than 1 slot left, we hide the window, no reason to
+ * show an empty window */
+ if (job_queue->n_slots < 1)
+ gtk_widget_hide_all(job_queue->window);
+
+ /* We resize the window to 1,1 to make it as small as _possible_ to
+ * avoid blank space after removing a slot */
+ gtk_window_resize(GTK_WINDOW(job_queue->window), 1, 1);
+
+ gdk_threads_leave();
+ g_mutex_unlock(job_queue->lock);
+}
+
+/**
+ * A function to consume jobs, this should run in its own thread
+ * @note Will never return
+ */
+static void
+job_consumer(gpointer data, gpointer unused)
+{
+ gpointer result;
+
+ RSJob *job = (RSJob *) data;
+ RSJobQueueSlot *slot = rs_job_queue_add_slot(job->job_queue);
+
+ /* Call the job */
+ if (!job->done)
+ job->result = job->func(slot, job->data);
+
+ rs_job_queue_remove_slot(job->job_queue, slot);
+ g_object_unref(job->job_queue);
+
+ if (job->done_cond)
+ {
+ /* If we take this path, we shouldn't free the job, rs_job_queue_wait()
+ * must take care of that */
+ g_mutex_lock(job->done_mutex);
+ job->done = TRUE;
+ g_cond_signal(job->done_cond);
+ g_mutex_unlock(job->done_mutex);
+ }
+ else
+ g_free(job);
+}
+
+/**
+ * Add a new job to the job queue
+ * @note When func is called, it WILL be from another thread, it may be
+ * required to acquire the GDK lock if any GTK+ stuff is done in the
+ * callback!
+ * @param func A function to call for performing the job
+ * @param data Data to pass to func
+ * @param waitable If TRUE, rs_job_queue_wait() will wait until completion
+ */
+RSJob *
+rs_job_queue_add_job(RSJobFunc func, gpointer data, gboolean waitable)
+{
+ RSJobQueue *job_queue = rs_job_queue_get_singleton();
+
+ g_assert(func != NULL);
+
+ g_mutex_lock(job_queue->lock);
+
+ RSJob *job = g_new0(RSJob, 1);
+ job->func = func;
+ job->job_queue = g_object_ref(job_queue);
+ job->data = data;
+ job->done = FALSE;
+
+ if (waitable)
+ {
+ job->done_cond = g_cond_new();
+ job->done_mutex = g_mutex_new();
+ }
+ else
+ {
+ job->done_cond = NULL;
+ job->done_mutex = NULL;
+ }
+
+ g_thread_pool_push(job_queue->pool, job, NULL);
+ g_mutex_unlock(job_queue->lock);
+}
+
+/**
+ * Wait (hang) until a job is finished and then free the memory allocated to job
+ * @param job The RSJob to wait for
+ * @return The value returned by the func given to rs_job_queue_add()
+ */
+gpointer
+rs_job_queue_wait(RSJob *job)
+{
+ gpointer result = NULL;
+
+ g_assert(job != NULL);
+ g_assert(job->done_cond != NULL);
+ g_assert(job->done_mutex != NULL);
+
+ /* Wait for it */
+ g_mutex_lock(job->done_mutex);
+ while(!job->done)
+ g_cond_wait(job->done_cond, job->done_mutex);
+ g_mutex_unlock(job->done_mutex);
+
+ /* Free everything */
+ g_cond_free(job->done_cond);
+ g_mutex_free(job->done_mutex);
+ g_free(job);
+
+ result = job->result;
+
+ return result;
+}
+
+/**
+ * Update the job description
+ * @note You should NOT have aquired the GDK thread lock when calling this
+ * function.
+ * @param slot A job_slot as recieved in the job callback function
+ * @param description The new description or NULL to show nothing
+ */
+void
+rs_job_update_description(RSJobQueueSlot *slot, const gchar *description)
+{
+ gdk_threads_enter();
+
+ if (description)
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), description);
+ else
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), "");
+
+ gdk_threads_leave();
+}
+
+/**
+ * Update the job progress bar
+ * @note You should NOT have aquired the GDK thread lock when calling this
+ * function.
+ * @param slot A job_slot as recieved in the job callback function
+ * @param fraction A value between 0.0 and 1.0 to set the progress bar at
+ * the specific fraction or -1.0 to pulse the progress bar.
+ */
+void
+rs_job_update_progress(RSJobQueueSlot *slot, const gdouble fraction)
+{
+ gdk_threads_enter();
+
+ if (fraction < 0.0)
+ gtk_progress_bar_pulse(GTK_PROGRESS_BAR(slot->progress));
+ else
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(slot->progress), fraction);
+
+ gdk_threads_leave();
+}
Added: trunk/librawstudio/rs-job-queue.h
===================================================================
--- trunk/librawstudio/rs-job-queue.h (rev 0)
+++ trunk/librawstudio/rs-job-queue.h 2009-02-24 02:58:03 UTC (rev 2221)
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2006-2009 Anders Brander <anders at brander.dk> and
+ * Anders Kvist <akv at lnxbx.dk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef RS_JOB_QUEUE_H
+#define RS_JOB_QUEUE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define RS_TYPE_JOB_QUEUE rs_job_queue_get_type()
+#define RS_JOB_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RS_TYPE_JOB_QUEUE, RSJobQueue))
+#define RS_JOB_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RS_TYPE_JOB_QUEUE, RSJobQueueClass))
+#define RS_IS_JOB_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RS_TYPE_JOB_QUEUE))
+#define RS_IS_JOB_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), RS_TYPE_JOB_QUEUE))
+#define RS_JOB_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RS_TYPE_JOB_QUEUE, RSJobQueueClass))
+
+typedef struct _RSJobQueueSlot RSJobQueueSlot;
+typedef struct _RSJob RSJob;
+typedef struct _RSJobQueue RSJobQueue;
+
+typedef struct {
+ GObjectClass parent_class;
+} RSJobQueueClass;
+
+typedef gpointer (*RSJobFunc)(RSJobQueueSlot *, gpointer);
+
+GType rs_job_queue_get_type(void);
+
+/**
+ * Add a new job to the job queue
+ * @note When func is called, it WILL be from another thread, it may be
+ * required to acquire the GDK lock if any GTK+ stuff is done in the
+ * callback!
+ * @param func A function to call for performing the job
+ * @param data Data to pass to func
+ * @param waitable If TRUE, rs_job_queue_wait() will wait until completion
+ */
+RSJob *
+rs_job_queue_add_job(RSJobFunc func, gpointer data, gboolean waitable);
+
+/**
+ * Wait (hang) until a job is finished and then free the memory allocated to job
+ * @param job The RSJob to wait for
+ * @return The value returned by the func given to rs_job_queue_add()
+ */
+gpointer
+rs_job_queue_wait(RSJob *job);
+
+/**
+ * Update the job description
+ * @note You should NOT have aquired the GDK thread lock when calling this
+ * function.
+ * @param slot A job_slot as recieved in the job callback function
+ * @param description The new description or NULL to show nothing
+ */
+void
+rs_job_update_description(RSJobQueueSlot *slot, const gchar *description);
+
+/**
+ * Update the job progress bar
+ * @note You should NOT have aquired the GDK thread lock when calling this
+ * function.
+ * @param slot A job_slot as recieved in the job callback function
+ * @param fraction A value between 0.0 and 1.0 to set the progress bar at
+ * the specific fraction or -1.0 to pulse the progress bar.
+ */
+void
+rs_job_update_progress(RSJobQueueSlot *slot, const gdouble fraction);
+
+G_END_DECLS
+
+#endif /* RS_JOB_QUEUE_H */
More information about the Rawstudio-commit
mailing list