2
* * Copyright (C) 2006-2011 Anders Brander <anders@brander.dk>,
3
* * Anders Kvist <akv@lnxbx.dk> and Klaus Post <klauspost@gmail.com>
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
#include <rawstudio.h>
20
#include "rs-job-queue.h"
22
struct _RSJobQueueSlot {
30
RSJobQueue *job_queue;
41
gboolean dispose_has_run;
50
G_DEFINE_TYPE (RSJobQueue, rs_job_queue, G_TYPE_OBJECT)
52
static void job_consumer(gpointer data, gpointer unused);
55
rs_job_queue_dispose (GObject *object)
57
RSJobQueue *job_queue = RS_JOB_QUEUE(object);
59
if (!job_queue->dispose_has_run)
61
job_queue->dispose_has_run = TRUE;
63
g_mutex_free(job_queue->lock);
67
G_OBJECT_CLASS(rs_job_queue_parent_class)->dispose(object);
71
rs_job_queue_class_init (RSJobQueueClass *klass)
73
GObjectClass *object_class = G_OBJECT_CLASS (klass);
75
object_class->dispose = rs_job_queue_dispose;
79
rs_job_queue_init(RSJobQueue *job_queue)
81
job_queue->dispose_has_run = FALSE;
82
job_queue->lock = g_mutex_new();
83
job_queue->pool = g_thread_pool_new(((GFunc) job_consumer), NULL, rs_get_number_of_processor_cores(), TRUE, NULL);
84
job_queue->n_slots = 0;
85
job_queue->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
86
job_queue->box = gtk_vbox_new(TRUE, 1);
88
gtk_container_add(GTK_CONTAINER(job_queue->window), job_queue->box);
90
gtk_window_set_accept_focus(GTK_WINDOW(job_queue->window), FALSE);
91
gtk_window_set_keep_above(GTK_WINDOW(job_queue->window), TRUE);
92
gtk_window_set_skip_pager_hint(GTK_WINDOW(job_queue->window), TRUE);
93
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(job_queue->window), TRUE);
94
gtk_window_set_title(GTK_WINDOW(job_queue->window), "");
95
gtk_window_set_type_hint(GTK_WINDOW(job_queue->window), GDK_WINDOW_TYPE_HINT_NOTIFICATION);
97
/* Let's spice it up a notch! :) */
98
#if GTK_CHECK_VERSION(2,12,0)
99
gtk_window_set_opacity(GTK_WINDOW(job_queue->window), 0.75);
102
#if GTK_CHECK_VERSION(2,10,0)
103
gtk_window_set_deletable(GTK_WINDOW(job_queue->window), FALSE);
106
/* Set the gravity, so that resizes will still result in a window
107
* positioned in the lower left */
108
gtk_window_set_gravity(GTK_WINDOW(job_queue->window), GDK_GRAVITY_SOUTH_EAST);
110
/* Place the window in lower left corner of screen */
111
gtk_window_move(GTK_WINDOW(job_queue->window), 0, gdk_screen_get_height(gdk_display_get_default_screen(gdk_display_get_default()))-50);
115
* Get a new RSJobQueue
116
* @return A new RSJobQueue
119
rs_job_queue_new(void)
121
return g_object_new (RS_TYPE_JOB_QUEUE, NULL);
125
* Return the RSJobQueue singleton
126
* @note THis function should be thread safe
127
* @return A RSJobQueue singleton
130
rs_job_queue_get_singleton(void)
132
static RSJobQueue *singleton = NULL;
133
static GStaticMutex lock = G_STATIC_MUTEX_INIT;
135
g_static_mutex_lock(&lock);
137
singleton = rs_job_queue_new();
138
g_static_mutex_unlock(&lock);
140
g_assert(RS_IS_JOB_QUEUE(singleton));
146
* Add a new processing slot to a RSJobQueue window
147
* @param job_queue A RSJobQueue
148
* @return A new RSJobQueueSlot
150
static RSJobQueueSlot *
151
rs_job_queue_add_slot(RSJobQueue *job_queue)
153
RSJobQueueSlot *slot = g_new0(RSJobQueueSlot, 1);
155
g_mutex_lock(job_queue->lock);
158
slot->container = gtk_vbox_new(FALSE, 0);
159
slot->progress = gtk_progress_bar_new();
160
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), "Hello...");
161
gtk_box_pack_start(GTK_BOX(slot->container), slot->progress, FALSE, TRUE, 1);
163
gtk_box_pack_start(GTK_BOX(job_queue->box), slot->container, FALSE, TRUE, 1);
165
/* If we previously got 0 slots open, position the window again */
166
if (job_queue->n_slots == 0)
167
gtk_window_move(GTK_WINDOW(job_queue->window), 0, gdk_screen_get_height(gdk_display_get_default_screen(gdk_display_get_default()))-50);
169
/* For some reason this must be called everytime to trigger correct placement?! */
170
gtk_widget_show_all(job_queue->window);
172
job_queue->n_slots++;
175
g_mutex_unlock(job_queue->lock);
181
* Remove and frees a RSJobQueueSlot from a RSJobQueue window
182
* @param job_queue A RSJobQueue
183
* @param slot The slot to remove and free
186
rs_job_queue_remove_slot(RSJobQueue *job_queue, RSJobQueueSlot *slot)
188
g_mutex_lock(job_queue->lock);
191
gtk_container_remove(GTK_CONTAINER(job_queue->box), slot->container);
192
job_queue->n_slots--;
194
/* If we got less than 1 slot left, we hide the window, no reason to
195
* show an empty window */
196
if (job_queue->n_slots < 1)
197
gtk_widget_hide_all(job_queue->window);
199
/* We resize the window to 1,1 to make it as small as _possible_ to
200
* avoid blank space after removing a slot */
201
gtk_window_resize(GTK_WINDOW(job_queue->window), 1, 1);
204
g_mutex_unlock(job_queue->lock);
208
* A function to consume jobs, this should run in its own thread
209
* @note Will never return
212
job_consumer(gpointer data, gpointer unused)
214
RSJob *job = (RSJob *) data;
215
RSJobQueueSlot *slot = rs_job_queue_add_slot(job->job_queue);
219
job->result = job->func(slot, job->data);
221
rs_job_queue_remove_slot(job->job_queue, slot);
222
g_object_unref(job->job_queue);
226
/* If we take this path, we shouldn't free the job, rs_job_queue_wait()
227
* must take care of that */
228
g_mutex_lock(job->done_mutex);
230
g_cond_signal(job->done_cond);
231
g_mutex_unlock(job->done_mutex);
238
* Add a new job to the job queue
239
* @note When func is called, it WILL be from another thread, it may be
240
* required to acquire the GDK lock if any GTK+ stuff is done in the
242
* @param func A function to call for performing the job
243
* @param data Data to pass to func
244
* @param waitable If TRUE, rs_job_queue_wait() will wait until completion
247
rs_job_queue_add_job(RSJobFunc func, gpointer data, gboolean waitable)
249
RSJobQueue *job_queue = rs_job_queue_get_singleton();
251
g_assert(func != NULL);
253
g_mutex_lock(job_queue->lock);
255
RSJob *job = g_new0(RSJob, 1);
257
job->job_queue = g_object_ref(job_queue);
263
job->done_cond = g_cond_new();
264
job->done_mutex = g_mutex_new();
268
job->done_cond = NULL;
269
job->done_mutex = NULL;
272
g_thread_pool_push(job_queue->pool, job, NULL);
273
g_mutex_unlock(job_queue->lock);
279
* Wait (hang) until a job is finished and then free the memory allocated to job
280
* @param job The RSJob to wait for
281
* @return The value returned by the func given to rs_job_queue_add()
284
rs_job_queue_wait(RSJob *job)
286
gpointer result = NULL;
288
g_assert(job != NULL);
289
g_assert(job->done_cond != NULL);
290
g_assert(job->done_mutex != NULL);
293
g_mutex_lock(job->done_mutex);
295
g_cond_wait(job->done_cond, job->done_mutex);
296
g_mutex_unlock(job->done_mutex);
298
/* Free everything */
299
g_cond_free(job->done_cond);
300
g_mutex_free(job->done_mutex);
303
result = job->result;
309
* Update the job description
310
* @note You should NOT have aquired the GDK thread lock when calling this
312
* @param slot A job_slot as recieved in the job callback function
313
* @param description The new description or NULL to show nothing
316
rs_job_update_description(RSJobQueueSlot *slot, const gchar *description)
321
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), description);
323
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(slot->progress), "");
329
* Update the job progress bar
330
* @note You should NOT have aquired the GDK thread lock when calling this
332
* @param slot A job_slot as recieved in the job callback function
333
* @param fraction A value between 0.0 and 1.0 to set the progress bar at
334
* the specific fraction or -1.0 to pulse the progress bar.
337
rs_job_update_progress(RSJobQueueSlot *slot, const gdouble fraction)
342
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(slot->progress));
344
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(slot->progress), fraction);