Commit a3e3d2c9 authored by Max Kellermann's avatar Max Kellermann

command: added command "idle"

"idle" waits until something noteworthy happens on the server, e.g. song change, playlist modified, database updated. This allows clients to keep up to date without polling.
parent 30c86d8a
......@@ -265,6 +265,27 @@ volume <int change>
change volume by amount _change_
NOTE: volume command is deprecated, use setvol instead
idle
Waits until there is a noteworthy change in one or more of
MPD's subsystems. As soon as there is one, it lists all
changed systems in a line in the format "changed: SUBSYSTEM",
where SUBSYSTEM is one of the following:
database: the song database has been updated
stored_playlist: a stored playlist has been modified, renamed,
created or deleted
playlist: the current playlist has been modified
player: the player has been started, stopped or seeked
mixer: the volume has been changed
output: an audio output has been enabled or disabled
options: options like "repeat", "random", "crossfade"
While a client waits for "idle" results, the server disables
timeouts, allowing a client to wait for events as long as mpd
runs. The "idle" command can be canceled by sending a new
command.
COMMAND LIST
------------
......
......@@ -45,6 +45,7 @@ mpd_headers = \
buffer2array.h \
charConv.h \
command.h \
idle.h \
condition.h \
conf.h \
crossfade.h \
......@@ -130,6 +131,7 @@ mpd_SOURCES = \
buffer2array.c \
charConv.c \
command.c \
idle.c \
condition.c \
conf.c \
crossfade.c \
......
......@@ -24,6 +24,7 @@
#include "log.h"
#include "path.h"
#include "client.h"
#include "idle.h"
#include "utils.h"
#include "os_compat.h"
......@@ -379,6 +380,7 @@ int enableAudioDevice(unsigned int device)
return -1;
audioDeviceStates[device] = true;
idle_add(IDLE_OUTPUT);
return 0;
}
......@@ -389,6 +391,7 @@ int disableAudioDevice(unsigned int device)
return -1;
audioDeviceStates[device] = false;
idle_add(IDLE_OUTPUT);
return 0;
}
......
......@@ -27,6 +27,7 @@
#include "ioops.h"
#include "main_notify.h"
#include "dlist.h"
#include "idle.h"
#include "../config.h"
......@@ -87,6 +88,13 @@ struct client {
size_t send_buf_used; /* bytes used this instance */
size_t send_buf_size; /* bytes usable this instance */
size_t send_buf_alloc; /* bytes actually allocated */
/** is this client waiting for an "idle" response? */
bool idle_waiting;
/** idle flags pending on this client, to be sent as soon as
the client enters "idle" */
unsigned idle_flags;
};
static LIST_HEAD(clients);
......@@ -409,6 +417,9 @@ static int client_input_received(struct client *client, int bytesRead)
int ret;
char *buf_tail = &(client->buffer[client->bufferLength - 1]);
/* any input from the client makes it leave "idle" mode */
client->idle_waiting = false;
while (bytesRead > 0) {
client->bufferLength++;
bytesRead--;
......@@ -635,7 +646,9 @@ void client_manager_expire(void)
if (client_is_expired(client)) {
DEBUG("client %i: expired\n", client->num);
client_close(client);
} else if (time(NULL) - client->lastTime >
} else if (!client->idle_waiting && /* idle clients
never expire */
time(NULL) - client->lastTime >
client_timeout) {
DEBUG("client %i: timeout\n", client->num);
client_close(client);
......@@ -807,3 +820,71 @@ mpd_fprintf void client_printf(struct client *client, const char *fmt, ...)
client_vprintf(client, fmt, args);
va_end(args);
}
static const char *const idle_names[] = {
"database",
"stored_playlist",
"playlist",
"player",
"mixer",
"output",
"options",
};
/**
* Send "idle" response to this client.
*/
static void
client_idle_notify(struct client *client)
{
unsigned flags, i;
assert(client->idle_waiting);
assert(client->idle_flags != 0);
flags = client->idle_flags;
client->idle_flags = 0;
client->idle_waiting = false;
for (i = 0; i < sizeof(idle_names) / sizeof(idle_names[0]); ++i) {
assert(idle_names[i] != NULL);
if (flags & (1 << i))
client_printf(client, "changed: %s\n",
idle_names[i]);
}
client_puts(client, "OK\n");
client->lastTime = time(NULL);
}
void client_manager_idle_add(unsigned flags)
{
struct client *client;
assert(flags != 0);
list_for_each_entry(client, &clients, siblings) {
if (client_is_expired(client))
continue;
client->idle_flags |= flags;
if (client->idle_waiting) {
client_idle_notify(client);
client_write_output(client);
}
}
}
bool client_idle_wait(struct client *client)
{
assert(!client->idle_waiting);
client->idle_waiting = true;
if (client->idle_flags != 0) {
client_idle_notify(client);
return true;
} else
return false;
}
......@@ -21,6 +21,7 @@
#include "gcc.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h>
#include <sys/socket.h>
......@@ -60,4 +61,17 @@ void client_vprintf(struct client *client, const char *fmt, va_list args);
*/
mpd_fprintf void client_printf(struct client *client, const char *fmt, ...);
/**
* Adds the specified idle flags to all clients and immediately sends
* notifications to all waiting clients.
*/
void client_manager_idle_add(unsigned flags);
/**
* Checks whether the client has pending idle flags. If yes, they are
* sent immediately and "true" is returned". If no, it puts the
* client into waiting mode and returns false.
*/
bool client_idle_wait(struct client *client);
#endif
......@@ -1239,6 +1239,17 @@ static int handlePlaylistAdd(struct client *client,
return print_playlist_result(client, result);
}
static int
handle_idle(struct client *client,
mpd_unused int argc, mpd_unused char *argv[])
{
/* enable "idle" mode on this client */
client_idle_wait(client);
/* return value is "1" so the caller won't print "OK" */
return 1;
}
void initCommands(void)
{
commandList = makeList(free, 1);
......@@ -1307,6 +1318,7 @@ void initCommands(void)
addCommand(COMMAND_TAGTYPES, PERMISSION_READ, 0, 0, handleTagTypes);
addCommand(COMMAND_COUNT, PERMISSION_READ, 2, -1, handleCount);
addCommand(COMMAND_RENAME, PERMISSION_CONTROL, 2, 2, handleRename);
addCommand("idle", PERMISSION_READ, 0, 0, handle_idle);
sortList(commandList);
}
......
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Support library for the "idle" command.
*
*/
#include "idle.h"
#include "main_notify.h"
#include <assert.h>
#include <pthread.h>
static unsigned idle_flags;
static pthread_mutex_t idle_mutex = PTHREAD_MUTEX_INITIALIZER;
void
idle_add(unsigned flags)
{
assert(flags != 0);
pthread_mutex_lock(&idle_mutex);
idle_flags |= flags;
pthread_mutex_unlock(&idle_mutex);
wakeup_main_task();
}
unsigned
idle_get(void)
{
unsigned flags;
pthread_mutex_lock(&idle_mutex);
flags = idle_flags;
idle_flags = 0;
pthread_mutex_unlock(&idle_mutex);
return flags;
}
/* the Music Player Daemon (MPD)
* Copyright (C) 2008 Max Kellermann <max@duempel.org>
* This project's homepage is: http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Support library for the "idle" command.
*
*/
#ifndef MPD_IDLE_H
#define MPD_IDLE_H
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,
};
/**
* Adds idle flag (with bitwise "or") and queues notifications to all
* clients.
*/
void
idle_add(unsigned flags);
/**
* Atomically reads and resets the global idle flags value.
*/
unsigned
idle_get(void);
#endif
......@@ -17,6 +17,7 @@
*/
#include "client.h"
#include "idle.h"
#include "command.h"
#include "playlist.h"
#include "database.h"
......@@ -445,9 +446,17 @@ int main(int argc, char *argv[])
while (COMMAND_RETURN_KILL != client_manager_io() &&
COMMAND_RETURN_KILL != handlePendingSignals()) {
unsigned flags;
syncPlayerAndPlaylist();
client_manager_expire();
reap_update_task();
/* send "idle" notificaions to all subscribed
clients */
flags = idle_get();
if (flags != 0)
client_manager_idle_add(flags);
}
write_state_file();
......
......@@ -21,6 +21,7 @@
#include "log.h"
#include "tag.h"
#include "song.h"
#include "idle.h"
#include "os_compat.h"
#include "main_notify.h"
......@@ -61,6 +62,8 @@ playerPlay(struct song *song)
pc.next_song = song;
player_command(PLAYER_COMMAND_PLAY);
idle_add(IDLE_PLAYER);
}
void pc_cancel(void)
......@@ -71,17 +74,23 @@ void pc_cancel(void)
void playerWait(void)
{
player_command(PLAYER_COMMAND_CLOSE_AUDIO);
idle_add(IDLE_PLAYER);
}
void playerKill(void)
{
player_command(PLAYER_COMMAND_EXIT);
idle_add(IDLE_PLAYER);
}
void playerPause(void)
{
if (pc.state != PLAYER_STATE_STOP)
if (pc.state != PLAYER_STATE_STOP) {
player_command(PLAYER_COMMAND_PAUSE);
idle_add(IDLE_PLAYER);
}
}
void playerSetPause(int pause_flag)
......@@ -185,6 +194,8 @@ playerSeek(struct song *song, float seek_time)
if (pc.error == PLAYER_ERROR_NOERROR) {
pc.seekWhere = seek_time;
player_command(PLAYER_COMMAND_SEEK);
idle_add(IDLE_PLAYER);
}
return 0;
......@@ -200,6 +211,8 @@ void setPlayerCrossFade(float crossFadeInSeconds)
if (crossFadeInSeconds < 0)
crossFadeInSeconds = 0;
pc.crossFade = crossFadeInSeconds;
idle_add(IDLE_OPTIONS);
}
void setPlayerSoftwareVolume(int volume)
......
......@@ -34,6 +34,7 @@
#include "state_file.h"
#include "storedPlaylist.h"
#include "ack.h"
#include "idle.h"
#include "os_compat.h"
#define PLAYLIST_STATE_STOP 0
......@@ -87,6 +88,8 @@ static void incrPlaylistVersion(void)
playlist.version = 1;
}
idle_add(IDLE_PLAYLIST);
}
void playlistVersionChange(void)
......@@ -504,6 +507,8 @@ static void syncPlaylistWithQueue(void)
if (pc.next_song == NULL && playlist.queued != -1) {
playlist.current = playlist.queued;
playlist.queued = -1;
idle_add(IDLE_PLAYER);
}
}
......@@ -951,6 +956,8 @@ void setPlaylistRepeatStatus(bool status)
clearPlayerQueue();
playlist.repeat = status;
idle_add(IDLE_OPTIONS);
}
enum playlist_result moveSongInPlaylist(int from, int to)
......@@ -1123,6 +1130,8 @@ void setPlaylistRandomStatus(bool status)
}
} else
orderPlaylist();
idle_add(IDLE_OPTIONS);
}
void previousSongInPlaylist(void)
......@@ -1219,6 +1228,7 @@ enum playlist_result savePlaylist(const char *utf8file)
while (fclose(fp) && errno == EINTR) ;
idle_add(IDLE_STORED_PLAYLIST);
return PLAYLIST_RESULT_SUCCESS;
}
......
......@@ -24,6 +24,7 @@
#include "utils.h"
#include "ls.h"
#include "database.h"
#include "idle.h"
#include "os_compat.h"
static ListNode *nodeOfStoredPlaylist(List *list, int idx)
......@@ -192,6 +193,7 @@ static int moveSongInStoredPlaylist(List *list, int src, int dest)
}
}
idle_add(IDLE_STORED_PLAYLIST);
return 0;
}
......@@ -212,6 +214,8 @@ moveSongInStoredPlaylistByPath(const char *utf8path, int src, int dest)
result = writeStoredPlaylistToPath(list, utf8path);
freeList(list);
idle_add(IDLE_STORED_PLAYLIST);
return result;
}
......@@ -231,6 +235,8 @@ removeAllFromStoredPlaylistByPath(const char *utf8path)
return PLAYLIST_RESULT_ERRNO;
while (fclose(file) != 0 && errno == EINTR);
idle_add(IDLE_STORED_PLAYLIST);
return PLAYLIST_RESULT_SUCCESS;
}
......@@ -262,6 +268,8 @@ removeOneSongFromStoredPlaylistByPath(const char *utf8path, int pos)
result = writeStoredPlaylistToPath(list, utf8path);
freeList(list);
idle_add(IDLE_STORED_PLAYLIST);
return result;
}
......@@ -299,6 +307,8 @@ appendSongToStoredPlaylistByPath(const char *utf8path, struct song *song)
playlist_print_song(file, song);
while (fclose(file) != 0 && errno == EINTR);
idle_add(IDLE_STORED_PLAYLIST);
return PLAYLIST_RESULT_SUCCESS;
}
......@@ -325,5 +335,6 @@ renameStoredPlaylist(const char *utf8from, const char *utf8to)
if (rename(from, to) < 0)
return PLAYLIST_RESULT_ERRNO;
idle_add(IDLE_STORED_PLAYLIST);
return PLAYLIST_RESULT_SUCCESS;
}
......@@ -30,6 +30,7 @@
#include "main_notify.h"
#include "condition.h"
#include "update.h"
#include "idle.h"
static enum update_progress {
UPDATE_PROGRESS_IDLE = 0,
......@@ -549,8 +550,10 @@ void reap_update_task(void)
if (pthread_join(update_thr, NULL))
FATAL("error joining update thread: %s\n", strerror(errno));
if (modified)
if (modified) {
playlistVersionChange();
idle_add(IDLE_DATABASE);
}
if (update_paths_nr) {
char *path = update_paths[0];
......
......@@ -21,6 +21,7 @@
#include "log.h"
#include "player_control.h"
#include "utils.h"
#include "idle.h"
#include "os_compat.h"
#include "../config.h"
......@@ -490,6 +491,8 @@ static int changeSoftwareVolume(int change, int rel)
int changeVolumeLevel(int change, int rel)
{
idle_add(IDLE_MIXER);
switch (volume_mixerType) {
#ifdef HAVE_ALSA
case VOLUME_MIXER_TYPE_ALSA:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment