changed the refresh hotkey
[giggle.git] / libgiggle / giggle-dispatcher.c
blobcc88e5966dbaf3928e303edc2e00b05da281139f
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2007 Imendio AB
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * 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 GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
21 #include <config.h>
22 #include <unistd.h>
24 #include "giggle-error.h"
25 #include "giggle-sysdeps.h"
26 #include "giggle-dispatcher.h"
28 #define d(x)
30 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GIGGLE_TYPE_DISPATCHER, GiggleDispatcherPriv))
32 typedef struct GiggleDispatcherPriv GiggleDispatcherPriv;
34 typedef struct {
35 gchar *command;
36 gchar *wd;
37 GiggleExecuteCallback callback;
38 guint id;
39 GPid pid;
40 gint std_out;
41 gint std_err;
42 gpointer user_data;
43 } DispatcherJob;
45 struct GiggleDispatcherPriv {
46 GQueue *queue;
48 DispatcherJob *current_job;
49 guint current_job_wait_id;
51 guint current_job_read_id;
52 GIOChannel *channel;
53 GString *output;
54 gsize length;
57 static void giggle_dispatcher_finalize (GObject *object);
60 static void dispatcher_queue_job (GiggleDispatcher *dispatcher,
61 DispatcherJob *job);
62 static void dispatcher_unqueue_job (GiggleDispatcher *dispatcher,
63 guint id);
64 static gboolean dispatcher_start_job (GiggleDispatcher *dispatcher,
65 DispatcherJob *job);
66 static void dispatcher_stop_current_job (GiggleDispatcher *dispatcher);
67 static void dispatcher_start_next_job (GiggleDispatcher *dispatcher);
68 static void dispatcher_signal_job_failed (GiggleDispatcher *dispatcher,
69 DispatcherJob *job,
70 GError *error);
71 static void dispatcher_job_free (DispatcherJob *job);
72 static gboolean dispatcher_is_free_to_start (GiggleDispatcher *dispatcher);
73 static gboolean dispatcher_is_current_job (GiggleDispatcher *dispatcher,
74 guint id);
76 static void dispatcher_job_finished_cb (GPid pid,
77 gint status,
78 GiggleDispatcher *dispatcher);
79 static gboolean dispatcher_job_read_cb (GIOChannel *source,
80 GIOCondition condition,
81 GiggleDispatcher *dispatcher);
84 G_DEFINE_TYPE (GiggleDispatcher, giggle_dispatcher, G_TYPE_OBJECT)
86 static void
87 giggle_dispatcher_class_init (GiggleDispatcherClass *class)
89 GObjectClass *object_class = G_OBJECT_CLASS (class);
91 object_class->finalize = giggle_dispatcher_finalize;
93 g_type_class_add_private (object_class, sizeof (GiggleDispatcherPriv));
96 static void
97 giggle_dispatcher_init (GiggleDispatcher *dispatcher)
99 GiggleDispatcherPriv *priv;
101 priv = GET_PRIV (dispatcher);
103 priv->queue = g_queue_new ();
104 priv->current_job = NULL;
105 priv->current_job_wait_id = 0;
108 static void
109 giggle_dispatcher_finalize (GObject *object)
111 GiggleDispatcher *dispatcher = GIGGLE_DISPATCHER (object);
112 GiggleDispatcherPriv *priv = GET_PRIV (object);
113 DispatcherJob *job;
115 if (priv->current_job_wait_id) {
116 dispatcher_stop_current_job (dispatcher);
119 while ((job = g_queue_pop_head (priv->queue))) {
120 dispatcher_job_free (job);
122 g_queue_free (priv->queue);
124 G_OBJECT_CLASS (giggle_dispatcher_parent_class)->finalize (object);
127 static void
128 dispatcher_queue_job (GiggleDispatcher *dispatcher, DispatcherJob *job)
130 GiggleDispatcherPriv *priv;
132 priv = GET_PRIV (dispatcher);
134 d(g_print ("GiggleDispatcher::queue_job\n"));
136 g_queue_push_tail (priv->queue, job);
139 static void
140 dispatcher_unqueue_job (GiggleDispatcher *dispatcher, guint id)
142 GiggleDispatcherPriv *priv;
143 GList *l;
145 priv = GET_PRIV (dispatcher);
147 d(g_print ("GiggleDispatcher::unqueue_job\n"));
149 for (l = priv->queue->head; l; l = l->next) {
150 DispatcherJob *job = (DispatcherJob *) l->data;
152 if (job->id == id) {
153 g_queue_delete_link (priv->queue, l);
154 dispatcher_job_free (job);
155 break;
160 static gboolean
161 dispatcher_start_job (GiggleDispatcher *dispatcher, DispatcherJob *job)
163 GiggleDispatcherPriv *priv;
164 gint argc;
165 gchar **argv;
166 GError *error = NULL;
168 priv = GET_PRIV (dispatcher);
170 g_assert (priv->current_job == NULL);
172 if (!g_shell_parse_argv (job->command, &argc, &argv, &error)) {
173 goto failed;
176 if (!g_spawn_async_with_pipes (job->wd, argv,
177 NULL, /* envp */
178 G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
179 NULL, NULL,
180 &job->pid,
181 NULL, &job->std_out, &job->std_err,
182 &error)) {
183 goto failed;
186 d(g_print ("GiggleDispatcher::run_job(job-started)\n"));
188 priv->channel = g_io_channel_unix_new (job->std_out);
189 g_io_channel_set_encoding (priv->channel, NULL, NULL);
190 priv->output = g_string_new ("");
191 priv->length = 0;
193 priv->current_job = job;
194 priv->current_job_read_id = g_io_add_watch_full (priv->channel,
195 G_PRIORITY_HIGH_IDLE,
196 G_IO_IN,
197 (GIOFunc) dispatcher_job_read_cb,
198 dispatcher, NULL);
199 priv->current_job_wait_id = g_child_watch_add (job->pid,
200 (GChildWatchFunc) dispatcher_job_finished_cb,
201 dispatcher);
202 g_strfreev (argv);
204 return TRUE;
206 failed:
207 dispatcher_signal_job_failed (dispatcher, job, error);
208 dispatcher_job_free (job);
209 g_strfreev (argv);
210 g_error_free (error);
211 priv->current_job = NULL;
212 priv->current_job_wait_id = 0;
214 return FALSE;
217 static void
218 dispatcher_stop_current_job (GiggleDispatcher *dispatcher)
220 GiggleDispatcherPriv *priv;
222 priv = GET_PRIV (dispatcher);
224 g_assert (priv->current_job_wait_id != 0);
225 g_source_remove (priv->current_job_wait_id);
226 priv->current_job_wait_id = 0;
228 g_source_remove (priv->current_job_read_id);
229 priv->current_job_read_id = 0;
231 g_io_channel_unref (priv->channel);
232 priv->channel = NULL;
234 g_string_free (priv->output, TRUE);
235 priv->output = NULL;
237 g_assert (priv->current_job != NULL);
238 giggle_sysdeps_kill_pid (priv->current_job->pid);
240 dispatcher_job_free (priv->current_job);
241 priv->current_job = NULL;
244 static void
245 dispatcher_start_next_job (GiggleDispatcher *dispatcher)
247 GiggleDispatcherPriv *priv;
248 DispatcherJob *job;
250 priv = GET_PRIV (dispatcher);
252 while ((job = (DispatcherJob *) g_queue_pop_head (priv->queue))) {
253 if (dispatcher_start_job (dispatcher, job)) {
254 break;
259 static void
260 dispatcher_signal_job_failed (GiggleDispatcher *dispatcher,
261 DispatcherJob *job,
262 GError *error)
264 job->callback (dispatcher, job->id, error, NULL, 0, job->user_data);
267 static void
268 dispatcher_job_free (DispatcherJob *job)
270 g_free (job->command);
271 g_free (job->wd);
273 if (job->pid) {
274 g_spawn_close_pid (job->pid);
277 if (job->std_out) {
278 close (job->std_out);
281 if (job->std_err) {
282 close (job->std_err);
285 g_slice_free (DispatcherJob, job);
288 static gboolean
289 dispatcher_is_free_to_start (GiggleDispatcher *dispatcher)
291 GiggleDispatcherPriv *priv;
293 priv = GET_PRIV (dispatcher);
295 if (priv->current_job) {
296 return FALSE;
299 return TRUE;
302 static gboolean
303 dispatcher_is_current_job (GiggleDispatcher *dispatcher, guint id)
305 GiggleDispatcherPriv *priv;
307 priv = GET_PRIV (dispatcher);
309 if (priv->current_job && priv->current_job->id == id) {
310 return TRUE;
313 return FALSE;
316 static void
317 dispatcher_job_finished_cb (GPid pid,
318 gint status,
319 GiggleDispatcher *dispatcher)
321 GiggleDispatcherPriv *priv;
322 DispatcherJob *job;
323 gchar *str;
324 gsize len;
326 priv = GET_PRIV (dispatcher);
327 job = priv->current_job;
329 d(g_print ("GiggleDispatcher::job_finished_cb\n"));
331 g_source_remove (priv->current_job_read_id);
332 priv->current_job_read_id = 0;
334 g_io_channel_read_to_end (priv->channel, &str, &len, NULL);
336 if (str) {
337 g_string_append_len (priv->output, str, len);
338 g_free (str);
341 job->callback (dispatcher, job->id, NULL,
342 priv->output->str, priv->length,
343 job->user_data);
345 dispatcher_job_free (job);
346 g_io_channel_unref (priv->channel);
347 g_string_free (priv->output, TRUE);
348 priv->current_job = NULL;
349 priv->current_job_wait_id = 0;
350 dispatcher_start_next_job (dispatcher);
353 static gboolean
354 dispatcher_job_read_cb (GIOChannel *source,
355 GIOCondition condition,
356 GiggleDispatcher *dispatcher)
358 GiggleDispatcherPriv *priv;
359 gsize length;
360 gchar *str;
361 gint count = 0;
362 GIOStatus status;
363 GError *error = NULL;
365 priv = GET_PRIV (dispatcher);
366 status = G_IO_STATUS_NORMAL;
368 while (count < 10 && status == G_IO_STATUS_NORMAL) {
369 status = g_io_channel_read_line (source, &str, &length, NULL, &error);
370 count++;
372 if (str) {
373 g_string_append_len (priv->output, str, length);
374 priv->length += length;
375 g_free (str);
379 if (status == G_IO_STATUS_ERROR) {
380 dispatcher_signal_job_failed (dispatcher, priv->current_job, error);
381 dispatcher_stop_current_job (dispatcher);
382 dispatcher_start_next_job (dispatcher);
384 return FALSE;
387 return TRUE;
390 GiggleDispatcher *
391 giggle_dispatcher_new (void)
393 return g_object_new (GIGGLE_TYPE_DISPATCHER, NULL);
396 guint
397 giggle_dispatcher_execute (GiggleDispatcher *dispatcher,
398 const gchar *wd,
399 const gchar *command,
400 GiggleExecuteCallback callback,
401 gpointer user_data)
403 DispatcherJob *job;
404 static guint id = 0;
406 g_return_val_if_fail (GIGGLE_IS_DISPATCHER (dispatcher), 0);
407 g_return_val_if_fail (command != NULL, 0);
408 g_return_val_if_fail (callback != NULL, 0);
410 job = g_slice_new0 (DispatcherJob);
412 job->command = g_strdup (command);
413 job->callback = callback;
414 job->user_data = user_data;
416 job->id = ++id;
417 job->pid = 0;
418 job->std_out = 0;
419 job->std_err = 0;
421 if (wd) {
422 job->wd = g_strdup (wd);
423 } else {
424 job->wd = NULL;
427 if (dispatcher_is_free_to_start (dispatcher)) {
428 dispatcher_start_job (dispatcher, job);
429 } else {
430 dispatcher_queue_job (dispatcher, job);
433 return job->id;
436 void
437 giggle_dispatcher_cancel (GiggleDispatcher *dispatcher, guint id)
439 g_return_if_fail (GIGGLE_IS_DISPATCHER (dispatcher));
440 g_return_if_fail (id > 0);
442 if (dispatcher_is_current_job (dispatcher, id)) {
443 dispatcher_stop_current_job (dispatcher);
444 dispatcher_start_next_job (dispatcher);
445 } else {
446 dispatcher_unqueue_job (dispatcher, id);