diff options
author | Max Kellermann <max@duempel.org> | 2008-10-14 22:38:14 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2008-10-14 22:38:14 +0200 |
commit | a3e3d2c9506d17b3e19e205535ec263ee75178c9 (patch) | |
tree | 3e93ea6ae33dba8a8f5e12e34629366dcdd7fdb7 /src | |
parent | 30c86d8ae64ae46ba3bcb1c63e867789feab6dc4 (diff) | |
download | mpd-a3e3d2c9506d17b3e19e205535ec263ee75178c9.tar.gz mpd-a3e3d2c9506d17b3e19e205535ec263ee75178c9.tar.xz mpd-a3e3d2c9506d17b3e19e205535ec263ee75178c9.zip |
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.
Diffstat (limited to '')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/audio.c | 3 | ||||
-rw-r--r-- | src/client.c | 83 | ||||
-rw-r--r-- | src/client.h | 14 | ||||
-rw-r--r-- | src/command.c | 12 | ||||
-rw-r--r-- | src/idle.c | 56 | ||||
-rw-r--r-- | src/idle.h | 64 | ||||
-rw-r--r-- | src/main.c | 9 | ||||
-rw-r--r-- | src/player_control.c | 15 | ||||
-rw-r--r-- | src/playlist.c | 10 | ||||
-rw-r--r-- | src/storedPlaylist.c | 11 | ||||
-rw-r--r-- | src/update.c | 5 | ||||
-rw-r--r-- | src/volume.c | 3 |
13 files changed, 284 insertions, 3 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 2151daa77..6135c41bb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/audio.c b/src/audio.c index ce62df46d..4305235d0 100644 --- a/src/audio.c +++ b/src/audio.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; } diff --git a/src/client.c b/src/client.c index e8f93b556..1dcec5af2 100644 --- a/src/client.c +++ b/src/client.c @@ -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; +} diff --git a/src/client.h b/src/client.h index 0d9f2e76a..50238d9f0 100644 --- a/src/client.h +++ b/src/client.h @@ -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 diff --git a/src/command.c b/src/command.c index 14ba2c300..1037c812d 100644 --- a/src/command.c +++ b/src/command.c @@ -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); } diff --git a/src/idle.c b/src/idle.c new file mode 100644 index 000000000..c779d0a91 --- /dev/null +++ b/src/idle.c @@ -0,0 +1,56 @@ +/* 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; +} diff --git a/src/idle.h b/src/idle.h new file mode 100644 index 000000000..69756b153 --- /dev/null +++ b/src/idle.h @@ -0,0 +1,64 @@ +/* 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 diff --git a/src/main.c b/src/main.c index b4f821008..1a7e73254 100644 --- a/src/main.c +++ b/src/main.c @@ -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(); diff --git a/src/player_control.c b/src/player_control.c index e7935f80f..086ef505a 100644 --- a/src/player_control.c +++ b/src/player_control.c @@ -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) diff --git a/src/playlist.c b/src/playlist.c index b160202bf..3498ae7bb 100644 --- a/src/playlist.c +++ b/src/playlist.c @@ -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; } diff --git a/src/storedPlaylist.c b/src/storedPlaylist.c index 3d5b8286f..fb0027599 100644 --- a/src/storedPlaylist.c +++ b/src/storedPlaylist.c @@ -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; } diff --git a/src/update.c b/src/update.c index 219806a4c..4017842b9 100644 --- a/src/update.c +++ b/src/update.c @@ -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]; diff --git a/src/volume.c b/src/volume.c index bf3e58f02..cca5860e3 100644 --- a/src/volume.c +++ b/src/volume.c @@ -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: |