I attach a new patch that integrates some fixes and ideas proposed by Qball.
This patch replaces the previous one.
I have added the support of idle command in Ario (
http://ario-player.sourceforge.net/) and I have found something that looks
like a bug in MPD:
- When a song is finished, the next one is played and the 'player' event is
emitted.
- When the client sends the status command just after this event, the songid
is the new one but the 'elapsed' time is not reseted to 0.
This is problem because I have implemented the solution using a timer on
client side to compute the elapsed time but with this bug the elapsed time
continues to be incremented on a new song.
I propose a patch to MPD to fix this issue but I don't really know this part
of MPD so it may not be the best solution.
Marc
2008/11/23 Marc Pavot <[EMAIL PROTECTED]>
> Hi,
>
>
>> Thanks, I have merged your patch. Please write a patch which adds
>> documentation for that feature to doc/protocol.xml.
>
> Done.
>
>
> I also attach a patch to libmpdclient that can be used as a basis for the
> discussion on 'idle' implementation on client side.
>
> Main points about this implementation:
> - It is designed to be integrated into client main loop. This solution has
> the advantage to avoid multithreading problems and to be well integrated
> into client applications. The disadvantage is that we have to implement
> something specific for each kind of loop. My patch comes with a generic
> callback mechanism to allow different implementations and with an
> implementation for GLib main loop.
> - The client application starts by initializing the main loop integration
> (mpd_glibInit)
> - It can then start or stop the idle mode (mpd_startIdle and mpd_stopIdle).
> In mpd_startIdle, the client specifies a callback to be called if a
> notification is received.
> - If the client application tries to send a command while it is in idle
> mode, 'noidle' is sent and the callback is called (if needed) before the
> real command.
>
> Hope this helps.
> Marc
>
diff --git a/libmpdclient.c b/libmpdclient.c
index 1c19dd8..2071af6 100644
--- a/libmpdclient.c
+++ b/libmpdclient.c
@@ -81,6 +81,17 @@
# define WSACleanup() do { /* nothing */ } while (0)
#endif
+static const char *const idle_names[] = {
+ "database",
+ "stored_playlist",
+ "playlist",
+ "player",
+ "mixer",
+ "output",
+ "options",
+ NULL
+};
+
#ifdef WIN32
static int winsock_dll_error(mpd_Connection *connection)
{
@@ -367,6 +378,13 @@ mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) {
connection->doneListOk = 0;
connection->returnElement = NULL;
connection->request = NULL;
+#ifdef MPD_GLIB
+ connection->source_id = 0;
+#endif
+ connection->idle = 0;
+ connection->startIdle = NULL;
+ connection->stopIdle = NULL;
+ connection->notify_cb = NULL;
if (winsock_dll_error(connection))
return connection;
@@ -446,6 +464,9 @@ static void mpd_executeCommand(mpd_Connection * connection, char * command) {
char * commandPtr = command;
int commandLen = strlen(command);
+ if (connection->idle)
+ mpd_stopIdle(connection);
+
if(!connection->doneProcessing && !connection->commandList) {
strcpy(connection->errorStr,"not done processing current command");
connection->error = 1;
@@ -1965,3 +1986,105 @@ void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
free(sPlaylist);
free(string);
}
+
+static void mpd_readChanges(mpd_Connection *connection)
+{
+ unsigned i;
+ unsigned flags = 0;
+ mpd_ReturnElement *re;
+
+ if (!connection->returnElement) mpd_getNextReturnElement(connection);
+
+ while (connection->returnElement) {
+ re = connection->returnElement;
+ if (re->name &&!strncmp (re->name, "changed", strlen ("changed"))) {
+ for (i = 0; idle_names[i]; ++i) {
+ if (!strcmp (re->value, idle_names[i])) {
+ flags |= (1 << i);
+ }
+ }
+ }
+ mpd_getNextReturnElement(connection);
+ }
+
+ /* Notifiy application */
+ if (connection->notify_cb && flags)
+ connection->notify_cb (connection, flags, connection->userdata);
+}
+
+void mpd_startIdle(mpd_Connection *connection, mpd_NotificationCb notify_cb, void *userdata)
+{
+ if (connection->idle)
+ return;
+
+ if (connection->startIdle)
+ connection->startIdle(connection);
+
+ mpd_executeCommand(connection, "idle\n");
+ connection->idle = 1;
+ connection->notify_cb = notify_cb;
+ connection->userdata = userdata;
+}
+
+void mpd_stopIdle(mpd_Connection *connection)
+{
+ if (connection->stopIdle)
+ connection->stopIdle(connection);
+
+ connection->idle = 0;
+ connection->notify_cb = NULL;
+ connection->doneProcessing = 1;
+ mpd_executeCommand(connection, "noidle\n");
+ mpd_readChanges(connection);
+}
+
+#ifdef MPD_GLIB
+static gboolean mpd_glibReadCb (GIOChannel *iochan, GIOCondition cond, gpointer data)
+{
+ mpd_Connection *connection = data;
+
+ if (!connection->idle) {
+ connection->source_id = 0;
+ return FALSE;
+ }
+
+ if ((cond & G_IO_IN)) {
+ connection->idle = 0;
+ if (connection->source_id) {
+ g_source_remove (connection->source_id);
+ connection->source_id = 0;
+ }
+ mpd_readChanges(connection);
+ }
+
+ return TRUE;
+}
+
+static void mpd_glibStartIdle(mpd_Connection *connection)
+{
+ static GIOChannel* iochan = NULL;
+
+ if (!iochan)
+ iochan = g_io_channel_unix_new (connection->sock);
+
+ connection->source_id = g_io_add_watch (iochan,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ mpd_glibReadCb,
+ connection);
+}
+
+static void mpd_glibStopIdle(mpd_Connection *connection)
+{
+ if (connection->source_id) {
+ g_source_remove (connection->source_id);
+ connection->source_id = 0;
+ }
+}
+
+void mpd_glibInit(mpd_Connection *connection)
+{
+ connection->startIdle = mpd_glibStartIdle;
+ connection->stopIdle = mpd_glibStopIdle;
+}
+#endif
+
diff --git a/libmpdclient.h b/libmpdclient.h
index 111d52a..cec0379 100644
--- a/libmpdclient.h
+++ b/libmpdclient.h
@@ -39,6 +39,10 @@
#include <sys/time.h>
#include <stdarg.h>
+#ifdef MPD_GLIB
+#include <glib.h>
+#endif
+
#define MPD_BUFFER_MAX_LENGTH 50000
#define MPD_ERRORSTR_MAX_LENGTH 1000
#define MPD_WELCOME_MESSAGE "OK MPD "
@@ -101,6 +105,30 @@ typedef struct _mpd_ReturnElement {
char * value;
} mpd_ReturnElement;
+enum {
+ /** song database has been updated*/
+ IDLE_DATABASE = 0x1,
+
+ /** a stored playlist has been modified, created, deleted or
+ renamed */
+ IDLE_STORED_PLAYLIST = 0x2,
+
+ /** the current playlist has been modified */
+ IDLE_PLAYLIST = 0x4,
+
+ /** the player state has changed: play, stop, pause, seek, ... */
+ IDLE_PLAYER = 0x8,
+
+ /** the volume has been modified */
+ IDLE_MIXER = 0x10,
+
+ /** an audio output device has been enabled or disabled */
+ IDLE_OUTPUT = 0x20,
+
+ /** options have changed: crossfade, random, repeat, ... */
+ IDLE_OPTIONS = 0x40,
+};
+
/* mpd_Connection
* holds info about connection to mpd
* use error, and errorStr to detect errors
@@ -126,8 +154,18 @@ typedef struct _mpd_Connection {
mpd_ReturnElement * returnElement;
struct timeval timeout;
char *request;
+ int idle;
+ void (*notify_cb) (struct _mpd_Connection *connection, unsigned flags, void *userdata);
+ void (*startIdle) (struct _mpd_Connection *connection);
+ void (*stopIdle) (struct _mpd_Connection *connection);
+ void *userdata;
+#ifdef MPD_GLIB
+ int source_id;
+#endif
} mpd_Connection;
+typedef void (*mpd_NotificationCb) (mpd_Connection *connection, unsigned flags, void *userdata);
+
/* mpd_newConnection
* use this to open a new connection
* you should use mpd_closeConnection, when your done with the connection,
@@ -663,6 +701,15 @@ void mpd_sendPlaylistMoveCommand(mpd_Connection *connection,
void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection,
char *playlist, int pos);
+
+void mpd_startIdle(mpd_Connection *connection, mpd_NotificationCb notify_cb, void *userdata);
+
+void mpd_stopIdle(mpd_Connection *connection);
+
+#ifdef MPD_GLIB
+void mpd_glibInit(mpd_Connection *connection);
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/src/playlist.c b/src/playlist.c
index 8581755..4bc6013 100644
--- a/src/playlist.c
+++ b/src/playlist.c
@@ -488,6 +488,7 @@ static void syncPlaylistWithQueue(void)
if (pc.next_song == NULL && playlist.queued != -1) {
playlist.current = playlist.queued;
playlist.queued = -1;
+ pc.elapsed_time = 0;
idle_add(IDLE_PLAYER);
}
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Musicpd-dev-team mailing list
Musicpd-dev-team@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team