screen_search: make SEARCH_ARTIST_TITLE part of an enum
[ncmpc.git] / src / gidle.c
blob415fb17f005fbbacdfc90d9f4662b50709695309
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.
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 <sys/select.h>
38 #include <errno.h>
40 struct mpd_glib_source {
41 struct mpd_connection *connection;
42 struct mpd_async *async;
43 struct mpd_parser *parser;
45 mpd_glib_callback_t callback;
46 void *callback_ctx;
48 GIOChannel *channel;
50 enum mpd_async_event io_events;
52 guint id;
54 enum mpd_idle idle_events;
56 /**
57 * This flag is a hack: it is set while mpd_glib_leave() is
58 * executed. mpd_glib_leave() might invoke the callback, and
59 * the callback might invoke mpd_glib_enter(), awkwardly
60 * leaving mpd_glib_leave() in idle mode. As long as this
61 * flag is set, mpd_glib_enter() is a no-op to prevent this.
63 bool leaving;
65 /**
66 * This flag is true when mpd_glib_free() has been called
67 * during a callback invoked from mpd_glib_leave().
68 * mpd_glib_leave() will do the real g_free() call then.
70 bool destroyed;
73 struct mpd_glib_source *
74 mpd_glib_new(struct mpd_connection *connection,
75 mpd_glib_callback_t callback, void *callback_ctx)
77 struct mpd_glib_source *source = g_new(struct mpd_glib_source, 1);
79 source->connection = connection;
80 source->async = mpd_connection_get_async(connection);
81 source->parser = mpd_parser_new();
82 /* XXX check source->parser!=NULL */
84 source->callback = callback;
85 source->callback_ctx = callback_ctx;
87 source->channel = g_io_channel_unix_new(mpd_async_get_fd(source->async));
88 source->io_events = 0;
89 source->id = 0;
90 source->leaving = false;
91 source->destroyed = false;
93 return source;
96 void
97 mpd_glib_free(struct mpd_glib_source *source)
99 assert(!source->destroyed);
101 if (source->id != 0)
102 g_source_remove(source->id);
104 g_io_channel_unref(source->channel);
106 mpd_parser_free(source->parser);
108 if (source->leaving)
109 source->destroyed = true;
110 else
111 g_free(source);
114 static void
115 mpd_glib_invoke(const struct mpd_glib_source *source)
117 assert(source->id == 0);
118 assert(!source->destroyed);
120 if (source->idle_events != 0)
121 source->callback(MPD_ERROR_SUCCESS, 0, NULL,
122 source->idle_events, source->callback_ctx);
125 static void
126 mpd_glib_invoke_error(const struct mpd_glib_source *source,
127 enum mpd_error error, enum mpd_server_error server_error,
128 const char *message)
130 assert(source->id == 0);
131 assert(!source->destroyed);
133 source->callback(error, server_error, message,
134 0, source->callback_ctx);
137 static void
138 mpd_glib_invoke_async_error(const struct mpd_glib_source *source)
140 assert(source->id == 0);
142 mpd_glib_invoke_error(source, mpd_async_get_error(source->async), 0,
143 mpd_async_get_error_message(source->async));
147 * Converts a GIOCondition bit mask to #mpd_async_event.
149 static enum mpd_async_event
150 g_io_condition_to_mpd_async_event(GIOCondition condition)
152 enum mpd_async_event events = 0;
154 if (condition & G_IO_IN)
155 events |= MPD_ASYNC_EVENT_READ;
157 if (condition & G_IO_OUT)
158 events |= MPD_ASYNC_EVENT_WRITE;
160 if (condition & G_IO_HUP)
161 events |= MPD_ASYNC_EVENT_HUP;
163 if (condition & G_IO_ERR)
164 events |= MPD_ASYNC_EVENT_ERROR;
166 return events;
170 * Converts a #mpd_async_event bit mask to GIOCondition.
172 static GIOCondition
173 mpd_async_events_to_g_io_condition(enum mpd_async_event events)
175 GIOCondition condition = 0;
177 if (events & MPD_ASYNC_EVENT_READ)
178 condition |= G_IO_IN;
180 if (events & MPD_ASYNC_EVENT_WRITE)
181 condition |= G_IO_OUT;
183 if (events & MPD_ASYNC_EVENT_HUP)
184 condition |= G_IO_HUP;
186 if (events & MPD_ASYNC_EVENT_ERROR)
187 condition |= G_IO_ERR;
189 return condition;
193 * Parses a response line from MPD.
195 * @return true on success, false on error
197 static bool
198 mpd_glib_feed(struct mpd_glib_source *source, char *line)
200 enum mpd_parser_result result;
202 result = mpd_parser_feed(source->parser, line);
203 switch (result) {
204 case MPD_PARSER_MALFORMED:
205 source->id = 0;
206 source->io_events = 0;
208 mpd_glib_invoke_error(source, MPD_ERROR_MALFORMED, 0,
209 "Malformed MPD response");
210 return false;
212 case MPD_PARSER_SUCCESS:
213 source->id = 0;
214 source->io_events = 0;
216 mpd_glib_invoke(source);
217 return false;
219 case MPD_PARSER_ERROR:
220 source->id = 0;
221 source->io_events = 0;
223 mpd_glib_invoke_error(source, MPD_ERROR_SERVER,
224 mpd_parser_get_server_error(source->parser),
225 mpd_parser_get_message(source->parser));
226 return false;
228 case MPD_PARSER_PAIR:
229 if (strcmp(mpd_parser_get_name(source->parser),
230 "changed") == 0)
231 source->idle_events |=
232 mpd_idle_name_parse(mpd_parser_get_value(source->parser));
234 break;
237 return true;
241 * Receives and evaluates a portion of the MPD response.
243 * @return true on success, false on error
245 static bool
246 mpd_glib_recv(struct mpd_glib_source *source)
248 char *line;
249 bool success;
251 while ((line = mpd_async_recv_line(source->async)) != NULL) {
252 success = mpd_glib_feed(source, line);
253 if (!success)
254 return false;
257 if (mpd_async_get_error(source->async) != MPD_ERROR_SUCCESS) {
258 source->id = 0;
259 source->io_events = 0;
261 mpd_glib_invoke_async_error(source);
262 return false;
265 return true;
268 static gboolean
269 mpd_glib_source_callback(G_GNUC_UNUSED GIOChannel *_source,
270 GIOCondition condition, gpointer data)
272 struct mpd_glib_source *source = data;
273 bool success;
274 enum mpd_async_event events;
276 assert(source->id != 0);
277 assert(source->io_events != 0);
279 /* let libmpdclient do some I/O */
281 success = mpd_async_io(source->async,
282 g_io_condition_to_mpd_async_event(condition));
283 if (!success) {
284 source->id = 0;
285 source->io_events = 0;
287 mpd_glib_invoke_async_error(source);
288 return false;
291 /* receive the response */
293 if ((condition & G_IO_IN) != 0) {
294 success = mpd_glib_recv(source);
295 if (!success)
296 return false;
299 /* continue polling? */
301 events = mpd_async_events(source->async);
302 if (events == 0) {
303 /* no events - disable watch */
304 source->id = 0;
305 source->io_events = 0;
307 return false;
308 } else if (events != source->io_events) {
309 /* different event mask: make new watch */
311 g_source_remove(source->id);
313 condition = mpd_async_events_to_g_io_condition(events);
314 source->id = g_io_add_watch(source->channel, condition,
315 mpd_glib_source_callback, source);
316 source->io_events = events;
318 return false;
319 } else
320 /* same event mask as before, enable the old watch */
321 return true;
324 static void
325 mpd_glib_add_watch(struct mpd_glib_source *source)
327 enum mpd_async_event events = mpd_async_events(source->async);
328 GIOCondition condition;
330 assert(source->io_events == 0);
331 assert(source->id == 0);
333 condition = mpd_async_events_to_g_io_condition(events);
335 source->id = g_io_add_watch(source->channel, condition,
336 mpd_glib_source_callback, source);
337 source->io_events = events;
340 bool
341 mpd_glib_enter(struct mpd_glib_source *source)
343 bool success;
345 assert(source->io_events == 0);
346 assert(source->id == 0);
347 assert(!source->destroyed);
349 if (source->leaving)
350 return false;
352 source->idle_events = 0;
354 success = mpd_async_send_command(source->async, "idle", NULL);
355 if (!success) {
356 mpd_glib_invoke_async_error(source);
357 return false;
360 mpd_glib_add_watch(source);
361 return true;
364 bool
365 mpd_glib_leave(struct mpd_glib_source *source)
367 enum mpd_idle events;
369 assert(!source->destroyed);
371 if (source->id == 0)
372 /* already left, callback was invoked */
373 return true;
375 g_source_remove(source->id);
376 source->id = 0;
377 source->io_events = 0;
379 events = source->idle_events == 0
380 ? mpd_run_noidle(source->connection)
381 : mpd_recv_idle(source->connection, false);
383 source->leaving = true;
385 if (events == 0 &&
386 mpd_connection_get_error(source->connection) != MPD_ERROR_SUCCESS) {
387 enum mpd_error error =
388 mpd_connection_get_error(source->connection);
389 enum mpd_server_error server_error =
390 error == MPD_ERROR_SERVER
391 ? mpd_connection_get_server_error(source->connection)
392 : 0;
394 mpd_glib_invoke_error(source, error, server_error,
395 mpd_connection_get_error_message(source->connection));
397 if (source->destroyed) {
398 g_free(source);
399 return false;
402 source->leaving = false;
403 return true;
406 source->idle_events |= events;
407 mpd_glib_invoke(source);
409 if (source->destroyed) {
410 g_free(source);
411 return false;
414 source->leaving = false;
415 return true;