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/doc/protocol.xml b/doc/protocol.xml
index 49f6d97..304b492 100644
--- a/doc/protocol.xml
+++ b/doc/protocol.xml
@@ -118,6 +118,7 @@
           <term>
             <cmdsynopsis>
               <command>idle</command>
+              <arg choice="opt" rep="repeat"><replaceable>SUBSYSTEMS</replaceable></arg>
             </cmdsynopsis>
           </term>
           <listitem>
@@ -184,6 +185,11 @@
               <command>idle</command> mode and print results
               immediately; might be empty at this time.
             </para>
+            <para>
+              If the optional <varname>SUBSYSTEMS</varname> argument is used,
+              MPD will only send notifications when something changed in
+              one of the specified subsytems.
+            </para>
           </listitem>
         </varlistentry>
         <varlistentry id="command_status">
diff --git a/libmpdclient.c b/libmpdclient.c
index 1c19dd8..32ca719 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,100 @@ 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);
+}
+
+void mpd_startIdle(mpd_Connection *connection, mpd_NotificationCb notify_cb)
+{
+	if (connection->startIdle)
+		connection->startIdle(connection);
+
+	mpd_executeCommand(connection, "idle\n");
+	connection->idle = 1;
+	connection->notify_cb = notify_cb;
+}
+
+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)
+{
+	GIOChannel* 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);
+	g_io_channel_unref (iochan);
+}
+
+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..31a9e3c 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,17 @@ typedef struct _mpd_Connection {
 	mpd_ReturnElement * returnElement;
 	struct timeval timeout;
 	char *request;
+	int idle;
+	void (*notify_cb) (struct _mpd_Connection *connection, unsigned flags);
+	void (*startIdle) (struct _mpd_Connection *connection);
+	void (*stopIdle) (struct _mpd_Connection *connection);
+#ifdef MPD_GLIB
+        int source_id;
+#endif
 } mpd_Connection;
 
+typedef void (*mpd_NotificationCb) (mpd_Connection *connection, unsigned flags);
+
 /* mpd_newConnection
  * use this to open a new connection
  * you should use mpd_closeConnection, when your done with the connection,
@@ -663,6 +700,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 mpd_stopIdle(mpd_Connection *connection);
+
+#ifdef MPD_GLIB
+void mpd_glibInit(mpd_Connection *connection);
+#endif
+
 #ifdef __cplusplus
 }
 #endif
-------------------------------------------------------------------------
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

Reply via email to