Code

gidle.c: Removed unused header
[ncmpc.git] / src / gidle.c
1 /* ncmpc (Ncurses MPD Client)
2    (c) 2004-2010 The Music Player Daemon Project
3    Project homepage: http://musicpd.org
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
16    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
20    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
29 #include "gidle.h"
31 #include <mpd/async.h>
32 #include <mpd/parser.h>
34 #include <glib.h>
35 #include <assert.h>
36 #include <string.h>
37 #include <errno.h>
39 struct mpd_glib_source {
40         struct mpd_connection *connection;
41         struct mpd_async *async;
42         struct mpd_parser *parser;
44         mpd_glib_callback_t callback;
45         void *callback_ctx;
47         GIOChannel *channel;
49         enum mpd_async_event io_events;
51         guint id;
53         enum mpd_idle idle_events;
55         /**
56          * This flag is a hack: it is set while mpd_glib_leave() is
57          * executed.  mpd_glib_leave() might invoke the callback, and
58          * the callback might invoke mpd_glib_enter(), awkwardly
59          * leaving mpd_glib_leave() in idle mode.  As long as this
60          * flag is set, mpd_glib_enter() is a no-op to prevent this.
61          */
62         bool leaving;
64         /**
65          * This flag is true when mpd_glib_free() has been called
66          * during a callback invoked from mpd_glib_leave().
67          * mpd_glib_leave() will do the real g_free() call then.
68          */
69         bool destroyed;
70 };
72 struct mpd_glib_source *
73 mpd_glib_new(struct mpd_connection *connection,
74              mpd_glib_callback_t callback, void *callback_ctx)
75 {
76         struct mpd_glib_source *source = g_new(struct mpd_glib_source, 1);
78         source->connection = connection;
79         source->async = mpd_connection_get_async(connection);
80         source->parser = mpd_parser_new();
81         /* XXX check source->parser!=NULL */
83         source->callback = callback;
84         source->callback_ctx = callback_ctx;
86         source->channel = g_io_channel_unix_new(mpd_async_get_fd(source->async));
87         source->io_events = 0;
88         source->id = 0;
89         source->leaving = false;
90         source->destroyed = false;
92         return source;
93 }
95 void
96 mpd_glib_free(struct mpd_glib_source *source)
97 {
98         assert(!source->destroyed);
100         if (source->id != 0)
101                 g_source_remove(source->id);
103         g_io_channel_unref(source->channel);
105         mpd_parser_free(source->parser);
107         if (source->leaving)
108                 source->destroyed = true;
109         else
110                 g_free(source);
113 static void
114 mpd_glib_invoke(const struct mpd_glib_source *source)
116         assert(source->id == 0);
117         assert(!source->destroyed);
119         if (source->idle_events != 0)
120                 source->callback(MPD_ERROR_SUCCESS, 0, NULL,
121                                  source->idle_events, source->callback_ctx);
124 static void
125 mpd_glib_invoke_error(const struct mpd_glib_source *source,
126                       enum mpd_error error, enum mpd_server_error server_error,
127                       const char *message)
129         assert(source->id == 0);
130         assert(!source->destroyed);
132         source->callback(error, server_error, message,
133                          0, source->callback_ctx);
136 static void
137 mpd_glib_invoke_async_error(const struct mpd_glib_source *source)
139         assert(source->id == 0);
141         mpd_glib_invoke_error(source, mpd_async_get_error(source->async), 0,
142                               mpd_async_get_error_message(source->async));
145 /**
146  * Converts a GIOCondition bit mask to #mpd_async_event.
147  */
148 static enum mpd_async_event
149 g_io_condition_to_mpd_async_event(GIOCondition condition)
151         enum mpd_async_event events = 0;
153         if (condition & G_IO_IN)
154                 events |= MPD_ASYNC_EVENT_READ;
156         if (condition & G_IO_OUT)
157                 events |= MPD_ASYNC_EVENT_WRITE;
159         if (condition & G_IO_HUP)
160                 events |= MPD_ASYNC_EVENT_HUP;
162         if (condition & G_IO_ERR)
163                 events |= MPD_ASYNC_EVENT_ERROR;
165         return events;
168 /**
169  * Converts a #mpd_async_event bit mask to GIOCondition.
170  */
171 static GIOCondition
172 mpd_async_events_to_g_io_condition(enum mpd_async_event events)
174         GIOCondition condition = 0;
176         if (events & MPD_ASYNC_EVENT_READ)
177                 condition |= G_IO_IN;
179         if (events & MPD_ASYNC_EVENT_WRITE)
180                 condition |= G_IO_OUT;
182         if (events & MPD_ASYNC_EVENT_HUP)
183                 condition |= G_IO_HUP;
185         if (events & MPD_ASYNC_EVENT_ERROR)
186                 condition |= G_IO_ERR;
188         return condition;
191 /**
192  * Parses a response line from MPD.
193  *
194  * @return true on success, false on error
195  */
196 static bool
197 mpd_glib_feed(struct mpd_glib_source *source, char *line)
199         enum mpd_parser_result result;
201         result = mpd_parser_feed(source->parser, line);
202         switch (result) {
203         case MPD_PARSER_MALFORMED:
204                 source->id = 0;
205                 source->io_events = 0;
207                 mpd_glib_invoke_error(source, MPD_ERROR_MALFORMED, 0,
208                                       "Malformed MPD response");
209                 return false;
211         case MPD_PARSER_SUCCESS:
212                 source->id = 0;
213                 source->io_events = 0;
215                 mpd_glib_invoke(source);
216                 return false;
218         case MPD_PARSER_ERROR:
219                 source->id = 0;
220                 source->io_events = 0;
222                 mpd_glib_invoke_error(source, MPD_ERROR_SERVER,
223                                       mpd_parser_get_server_error(source->parser),
224                                       mpd_parser_get_message(source->parser));
225                 return false;
227         case MPD_PARSER_PAIR:
228                 if (strcmp(mpd_parser_get_name(source->parser),
229                            "changed") == 0)
230                         source->idle_events |=
231                                 mpd_idle_name_parse(mpd_parser_get_value(source->parser));
233                 break;
234         }
236         return true;
239 /**
240  * Receives and evaluates a portion of the MPD response.
241  *
242  * @return true on success, false on error
243  */
244 static bool
245 mpd_glib_recv(struct mpd_glib_source *source)
247         char *line;
248         bool success;
250         while ((line = mpd_async_recv_line(source->async)) != NULL) {
251                 success = mpd_glib_feed(source, line);
252                 if (!success)
253                         return false;
254         }
256         if (mpd_async_get_error(source->async) != MPD_ERROR_SUCCESS) {
257                 source->id = 0;
258                 source->io_events = 0;
260                 mpd_glib_invoke_async_error(source);
261                 return false;
262         }
264         return true;
267 static gboolean
268 mpd_glib_source_callback(G_GNUC_UNUSED GIOChannel *_source,
269                          GIOCondition condition, gpointer data)
271         struct mpd_glib_source *source = data;
272         bool success;
273         enum mpd_async_event events;
275         assert(source->id != 0);
276         assert(source->io_events != 0);
278         /* let libmpdclient do some I/O */
280         success = mpd_async_io(source->async,
281                                g_io_condition_to_mpd_async_event(condition));
282         if (!success) {
283                 source->id = 0;
284                 source->io_events = 0;
286                 mpd_glib_invoke_async_error(source);
287                 return false;
288         }
290         /* receive the response */
292         if ((condition & G_IO_IN) != 0) {
293                 success = mpd_glib_recv(source);
294                 if (!success)
295                         return false;
296         }
298         /* continue polling? */
300         events = mpd_async_events(source->async);
301         if (events == 0) {
302                 /* no events - disable watch */
303                 source->id = 0;
304                 source->io_events = 0;
306                 return false;
307         } else if (events != source->io_events) {
308                 /* different event mask: make new watch */
310                 g_source_remove(source->id);
312                 condition = mpd_async_events_to_g_io_condition(events);
313                 source->id = g_io_add_watch(source->channel, condition,
314                                             mpd_glib_source_callback, source);
315                 source->io_events = events;
317                 return false;
318         } else
319                 /* same event mask as before, enable the old watch */
320                 return true;
323 static void
324 mpd_glib_add_watch(struct mpd_glib_source *source)
326         enum mpd_async_event events = mpd_async_events(source->async);
327         GIOCondition condition;
329         assert(source->io_events == 0);
330         assert(source->id == 0);
332         condition = mpd_async_events_to_g_io_condition(events);
334         source->id = g_io_add_watch(source->channel, condition,
335                                     mpd_glib_source_callback, source);
336         source->io_events = events;
339 bool
340 mpd_glib_enter(struct mpd_glib_source *source)
342         bool success;
344         assert(source->io_events == 0);
345         assert(source->id == 0);
346         assert(!source->destroyed);
348         if (source->leaving)
349                 return false;
351         source->idle_events = 0;
353         success = mpd_async_send_command(source->async, "idle", NULL);
354         if (!success) {
355                 mpd_glib_invoke_async_error(source);
356                 return false;
357         }
359         mpd_glib_add_watch(source);
360         return true;
363 bool
364 mpd_glib_leave(struct mpd_glib_source *source)
366         enum mpd_idle events;
368         assert(!source->destroyed);
370         if (source->id == 0)
371                 /* already left, callback was invoked */
372                 return true;
374         g_source_remove(source->id);
375         source->id = 0;
376         source->io_events = 0;
378         events = source->idle_events == 0
379                 ? mpd_run_noidle(source->connection)
380                 : mpd_recv_idle(source->connection, false);
382         source->leaving = true;
384         if (events == 0 &&
385             mpd_connection_get_error(source->connection) != MPD_ERROR_SUCCESS) {
386                 enum mpd_error error =
387                         mpd_connection_get_error(source->connection);
388                 enum mpd_server_error server_error =
389                         error == MPD_ERROR_SERVER
390                         ? mpd_connection_get_server_error(source->connection)
391                         : 0;
393                 mpd_glib_invoke_error(source, error, server_error,
394                                       mpd_connection_get_error_message(source->connection));
396                 if (source->destroyed) {
397                         g_free(source);
398                         return false;
399                 }
401                 source->leaving = false;
402                 return true;
403         }
405         source->idle_events |= events;
406         mpd_glib_invoke(source);
408         if (source->destroyed) {
409                 g_free(source);
410                 return false;
411         }
413         source->leaving = false;
414         return true;