diff --git a/src/mpdpp.cpp b/src/mpdpp.cpp index 14828532..37cb1704 100644 --- a/src/mpdpp.cpp +++ b/src/mpdpp.cpp @@ -18,6 +18,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ +#include #include #include @@ -37,6 +38,8 @@ Connection::Connection() : itsConnection(0), isCommandsListEnabled(0), itsErrorCode(0), itsMaxPlaylistLength(-1), + isIdle(0), + supportsIdle(0), itsHost("localhost"), itsPort(6600), itsTimeout(15), @@ -69,6 +72,13 @@ bool Connection::Connect() return false; if (!itsPassword.empty()) SendPassword(); + itsPoll.fd = mpd_connection_get_fd(itsConnection); + itsPoll.events = POLLIN; + supportsIdle = Version() > 13; + // in UpdateStatus() we compare it to itsElapsedTimer[0], + // and for the first time it has always evaluate to true + // so we need it to be zero at this point + itsElapsedTimer[1] = 0; return !CheckForErrors(); } @@ -88,6 +98,7 @@ void Connection::Disconnect() if (itsCurrentStatus) mpd_status_free(itsCurrentStatus); itsConnection = 0; + isIdle = 0; itsCurrentStatus = 0; itsOldStatus = 0; itsStats = 0; @@ -115,9 +126,12 @@ void Connection::SetHostname(const std::string &host) itsHost = host; } -bool Connection::SendPassword() const +bool Connection::SendPassword() { - return mpd_run_password(itsConnection, itsPassword.c_str()); + GoBusy(); + assert(!isCommandsListEnabled); + mpd_run_password(itsConnection, itsPassword.c_str()); + return !CheckForErrors(); } void Connection::SetStatusUpdater(StatusUpdater updater, void *data) @@ -132,11 +146,51 @@ void Connection::SetErrorHandler(ErrorHandler handler, void *data) itsErrorHandlerUserdata = data; } +void Connection::GoIdle() +{ + if (supportsIdle && !isIdle && mpd_send_idle(itsConnection)) + isIdle = 1; +} + +mpd_idle Connection::GoBusy() +{ + if (isIdle && mpd_send_noidle(itsConnection)) + { + isIdle = 0; + return mpd_recv_idle(itsConnection, 1); + } + return mpd_idle(0); +} + void Connection::UpdateStatus() { if (!itsConnection) return; + if (isIdle) + { + poll(&itsPoll, 1, 10); + if (itsPoll.revents & POLLIN) + GoBusy(); + else + { + // count local elapsed time as we don't receive + // this from mpd while being in idle mode + time(&itsElapsedTimer[1]); + double diff = difftime(itsElapsedTimer[1], itsElapsedTimer[0]); + if (diff >= 1.0 && Mpd.GetState() == psPlay) + { + time(&itsElapsedTimer[0]); + itsElapsed += diff; + StatusChanges changes; + changes.ElapsedTime = 1; + if (itsUpdater) + itsUpdater(this, changes, itsErrorHandlerUserdata); + } + return; + } + } + CheckForErrors(); if (!itsConnection) @@ -158,6 +212,32 @@ void Connection::UpdateStatus() if (itsCurrentStatus && itsUpdater) { + if (supportsIdle) + { + // sync local elapsed time counter with mpd + itsElapsed = mpd_status_get_elapsed_time(itsCurrentStatus); + // little hack as it seems mpd doesn't always return elapsed + // time equal to 0 even if song has changed, it sometimes + // returns the last second, so we need to bypass it by zeroing + // it in this case. + if (itsElapsed == mpd_status_get_total_time(itsCurrentStatus)) + itsElapsed = 0; + time(&itsElapsedTimer[0]); + } + else + { + time(&itsElapsedTimer[0]); + if (itsElapsedTimer[0] > itsElapsedTimer[1]) + { + unsigned mpd_elapsed = mpd_status_get_elapsed_time(itsCurrentStatus); + if (itsElapsed < mpd_elapsed-2 || itsElapsed+1 > mpd_elapsed) + itsElapsed = mpd_elapsed; + else if (Mpd.GetState() == psPlay) + ++itsElapsed; + time(&itsElapsedTimer[1]); + } + } + if (!itsOldStatus) { itsChanges.Playlist = 1; @@ -191,6 +271,7 @@ void Connection::UpdateStatus() itsChanges.StatusFlags = itsChanges.Repeat || itsChanges.Random || itsChanges.Single || itsChanges.Consume || itsChanges.Crossfade || itsChanges.DBUpdating; } itsUpdater(this, itsChanges, itsErrorHandlerUserdata); + GoIdle(); } } @@ -198,6 +279,8 @@ void Connection::UpdateStats() { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); if (itsStats) mpd_stats_free(itsStats); itsStats = mpd_run_stats(itsConnection); @@ -207,155 +290,317 @@ bool Connection::UpdateDirectory(const std::string &path) { if (!itsConnection) return false; - if (!mpd_run_update(itsConnection, path.c_str())) - return false; - UpdateStatus(); - return true; + if (!isCommandsListEnabled) + { + GoBusy(); + bool success = mpd_run_update(itsConnection, path.c_str()); + if (!supportsIdle && success) + UpdateStatus(); + return success; + } + else + { + assert(!isIdle); + return mpd_send_update(itsConnection, path.c_str()); + } + } -void Connection::Play() const +void Connection::Play() { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_play : mpd_run_play)(itsConnection); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_play(itsConnection); + } + else + { + assert(!isIdle); + mpd_send_play(itsConnection); + } } -void Connection::Play(int pos) const +void Connection::Play(int pos) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_play_pos : mpd_run_play_pos)(itsConnection, pos); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_play_pos(itsConnection, pos); + } + else + { + assert(!isIdle); + mpd_send_play_pos(itsConnection, pos); + } } -void Connection::PlayID(int id) const +void Connection::PlayID(int id) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_play_id : mpd_run_play_id)(itsConnection, id); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_play_id(itsConnection, id); + } + else + { + assert(!isIdle); + mpd_send_play_id(itsConnection, id); + } } -void Connection::Pause(bool state) const +void Connection::Pause(bool state) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_pause : mpd_run_pause)(itsConnection, state); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_pause(itsConnection, state); + } + else + { + assert(!isIdle); + mpd_send_pause(itsConnection, state); + } } -void Connection::Toggle() const +void Connection::Toggle() { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_toggle_pause : mpd_run_toggle_pause)(itsConnection); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_toggle_pause(itsConnection);; + } + else + { + assert(!isIdle); + mpd_send_toggle_pause(itsConnection); + } } -void Connection::Stop() const +void Connection::Stop() { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_stop : mpd_run_stop)(itsConnection); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_stop(itsConnection); + } + else + { + assert(!isIdle); + mpd_send_stop(itsConnection); + } } -void Connection::Next() const +void Connection::Next() { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_next : mpd_run_next)(itsConnection); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_next(itsConnection); + } + else + { + assert(!isIdle); + mpd_send_next(itsConnection); + } } -void Connection::Prev() const +void Connection::Prev() { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_previous : mpd_run_previous)(itsConnection); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_previous(itsConnection); + } + else + { + assert(!isIdle); + mpd_send_previous(itsConnection); + } } -void Connection::Move(unsigned from, unsigned to) const +void Connection::Move(unsigned from, unsigned to) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_move : mpd_run_move)(itsConnection, from, to); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_move(itsConnection, from, to); + } + else + { + assert(!isIdle); + mpd_send_move(itsConnection, from, to); + } } -void Connection::Swap(unsigned from, unsigned to) const +void Connection::Swap(unsigned from, unsigned to) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_swap : mpd_run_swap)(itsConnection, from, to); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_swap(itsConnection, from, to); + } + else + { + assert(!isIdle); + mpd_send_swap(itsConnection, from, to); + } } -void Connection::Seek(unsigned where) const +void Connection::Seek(unsigned where) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_seek_pos : mpd_run_seek_pos)(itsConnection, Mpd.GetCurrentSongPos(), where); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where); + } + else + { + assert(!isIdle); + mpd_send_seek_pos(itsConnection, Mpd.GetCurrentSongPos(), where); + } } -void Connection::Shuffle() const +void Connection::Shuffle() { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_shuffle : mpd_run_shuffle)(itsConnection); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_shuffle(itsConnection); + } + else + { + assert(!isIdle); + mpd_send_shuffle(itsConnection); + } } -void Connection::ClearPlaylist() const +void Connection::ClearPlaylist() { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_clear : mpd_run_clear)(itsConnection); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_clear(itsConnection); + } + else + { + assert(!isIdle); + mpd_send_clear(itsConnection); + } } -void Connection::ClearPlaylist(const std::string &playlist) const +void Connection::ClearPlaylist(const std::string &playlist) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_playlist_clear : mpd_run_playlist_clear)(itsConnection, playlist.c_str()); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_playlist_clear(itsConnection, playlist.c_str()); + } + else + { + mpd_send_playlist_clear(itsConnection, playlist.c_str()); + assert(!isIdle); + } } -void Connection::AddToPlaylist(const std::string &path, const Song &s) const +void Connection::AddToPlaylist(const std::string &path, const Song &s) { if (!s.Empty()) AddToPlaylist(path, s.GetFile()); } -void Connection::AddToPlaylist(const std::string &path, const std::string &file) const +void Connection::AddToPlaylist(const std::string &path, const std::string &file) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_playlist_add : mpd_run_playlist_add)(itsConnection, path.c_str(), file.c_str()); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_playlist_add(itsConnection, path.c_str(), file.c_str()); + } + else + { + assert(!isIdle); + mpd_send_playlist_add(itsConnection, path.c_str(), file.c_str()); + } } -void Connection::Move(const std::string &path, int from, int to) const +void Connection::Move(const std::string &path, int from, int to) { if (!itsConnection) return; + if (!isCommandsListEnabled) + GoBusy(); + else + assert(!isIdle); mpd_send_playlist_move(itsConnection, path.c_str(), from, to); if (!isCommandsListEnabled) mpd_response_finish(itsConnection); } -bool Connection::Rename(const std::string &from, const std::string &to) const +bool Connection::Rename(const std::string &from, const std::string &to) { if (!itsConnection) return false; - return (isCommandsListEnabled ? mpd_send_rename : mpd_run_rename)(itsConnection, from.c_str(), to.c_str()); + if (!isCommandsListEnabled) + { + GoBusy(); + return mpd_run_rename(itsConnection, from.c_str(), to.c_str()); + } + else + { + assert(!isIdle); + return mpd_send_rename(itsConnection, from.c_str(), to.c_str()); + } } -void Connection::GetPlaylistChanges(unsigned version, SongList &v) const +void Connection::GetPlaylistChanges(unsigned version, SongList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); if (!version) v.reserve(GetPlaylistLength()); + GoBusy(); mpd_send_queue_changes_meta(itsConnection, version); while (mpd_song *s = mpd_recv_song(itsConnection)) v.push_back(new Song(s, 1)); mpd_response_finish(itsConnection); } -Song Connection::GetSong(const std::string &path) const +Song Connection::GetSong(const std::string &path) { if (!itsConnection) return Song(); + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_list_all_meta(itsConnection, path.c_str()); mpd_song *s = mpd_recv_song(itsConnection); mpd_response_finish(itsConnection); @@ -367,60 +612,106 @@ int Connection::GetCurrentSongPos() const return itsCurrentStatus && isPlaying() ? mpd_status_get_song_pos(itsCurrentStatus) : -1; } -Song Connection::GetCurrentSong() const +Song Connection::GetCurrentSong() { + assert(!isCommandsListEnabled); + GoBusy(); return Song(itsConnection && isPlaying() ? mpd_run_current_song(itsConnection) : 0); } -void Connection::GetPlaylistContent(const std::string &path, SongList &v) const +void Connection::GetPlaylistContent(const std::string &path, SongList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_list_playlist_meta(itsConnection, path.c_str()); while (mpd_song *s = mpd_recv_song(itsConnection)) v.push_back(new Song(s)); mpd_response_finish(itsConnection); } -void Connection::SetRepeat(bool mode) const +void Connection::SetRepeat(bool mode) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_repeat : mpd_run_repeat)(itsConnection, mode); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_repeat(itsConnection, mode); + } + else + { + assert(!isIdle); + mpd_send_repeat(itsConnection, mode); + } } -void Connection::SetRandom(bool mode) const +void Connection::SetRandom(bool mode) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_random : mpd_run_random)(itsConnection, mode); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_random(itsConnection, mode); + } + else + { + assert(!isIdle); + mpd_send_random(itsConnection, mode); + } } -void Connection::SetSingle(bool mode) const +void Connection::SetSingle(bool mode) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_single : mpd_run_single)(itsConnection, mode); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_single(itsConnection, mode); + } + else + { + assert(!isIdle); + mpd_send_single(itsConnection, mode); + } } -void Connection::SetConsume(bool mode) const +void Connection::SetConsume(bool mode) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_consume : mpd_run_consume)(itsConnection, mode); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_consume(itsConnection, mode); + } + else + { + assert(!isIdle); + mpd_send_consume(itsConnection, mode); + } } void Connection::SetVolume(unsigned vol) { if (!itsConnection || vol > 100) return; - if (mpd_run_set_volume(itsConnection, vol) && itsUpdater) + assert(!isCommandsListEnabled); + GoBusy(); + if (mpd_run_set_volume(itsConnection, vol) && !supportsIdle) UpdateStatus(); } -std::string Connection::GetReplayGainMode() const +std::string Connection::GetReplayGainMode() { - if (!itsConnection || !mpd_send_command(itsConnection, "replay_gain_status", NULL)) + if (!itsConnection) + return "Unknown"; + assert(!isCommandsListEnabled); + GoBusy(); + if (!mpd_send_command(itsConnection, "replay_gain_status", NULL)) return "Unknown"; std::string result; if (mpd_pair *pair = mpd_recv_pair_named(itsConnection, "replay_gain_mode")) @@ -434,7 +725,7 @@ std::string Connection::GetReplayGainMode() const return result; } -void Connection::SetReplayGainMode(ReplayGainMode mode) const +void Connection::SetReplayGainMode(ReplayGainMode mode) { if (!itsConnection) return; @@ -453,17 +744,30 @@ void Connection::SetReplayGainMode(ReplayGainMode mode) const default: FatalError("undefined value of ReplayGainMode!"); } + if (!isCommandsListEnabled) + GoBusy(); + else + assert(!isIdle); if (!mpd_send_command(itsConnection, "replay_gain_mode", rg_mode, NULL)) return; if (!isCommandsListEnabled) mpd_response_finish(itsConnection); } -void Connection::SetCrossfade(unsigned crossfade) const +void Connection::SetCrossfade(unsigned crossfade) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_crossfade : mpd_run_crossfade)(itsConnection, crossfade); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_crossfade(itsConnection, crossfade); + } + else + { + assert(!isIdle); + mpd_send_crossfade(itsConnection, crossfade); + } } int Connection::AddSong(const std::string &path, int pos) @@ -473,6 +777,10 @@ int Connection::AddSong(const std::string &path, int pos) int id = -1; if (GetPlaylistLength() < itsMaxPlaylistLength) { + if (!isCommandsListEnabled) + GoBusy(); + else + assert(!isIdle); if (pos < 0) mpd_send_add_id(itsConnection, path.c_str()); else @@ -481,7 +789,6 @@ int Connection::AddSong(const std::string &path, int pos) { id = mpd_recv_song_id(itsConnection); mpd_response_finish(itsConnection); - UpdateStatus(); } else id = 0; @@ -496,21 +803,31 @@ int Connection::AddSong(const Song &s, int pos) return !s.Empty() ? (AddSong((!s.isFromDB() ? "file://" : "") + (s.Localized() ? locale_to_utf_cpy(s.GetFile()) : s.GetFile()), pos)) : -1; } -void Connection::Add(const std::string &path) const +void Connection::Add(const std::string &path) { if (!itsConnection) return; - mpd_send_add(itsConnection, path.c_str()); - mpd_response_finish(itsConnection); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_send_add(itsConnection, path.c_str()); + } + else + { + assert(!isIdle); + mpd_run_add(itsConnection, path.c_str()); + } } bool Connection::AddRandomSongs(size_t number) { if (!itsConnection && !number) return false; + assert(!isCommandsListEnabled); TagList files; + GoBusy(); mpd_send_list_all(itsConnection, "/"); while (mpd_pair *item = mpd_recv_pair_named(itsConnection, "file")) { @@ -538,35 +855,54 @@ bool Connection::AddRandomSongs(size_t number) return true; } -void Connection::Delete(unsigned pos) const +void Connection::Delete(unsigned pos) { if (!itsConnection) return; + if (!isCommandsListEnabled) + GoBusy(); + else + assert(!isIdle); mpd_send_delete(itsConnection, pos); if (!isCommandsListEnabled) mpd_response_finish(itsConnection); } -void Connection::DeleteID(unsigned id) const +void Connection::DeleteID(unsigned id) { if (!itsConnection) return; + if (!isCommandsListEnabled) + GoBusy(); + else + assert(!isIdle); mpd_send_delete_id(itsConnection, id); if (!isCommandsListEnabled) mpd_response_finish(itsConnection); } -void Connection::Delete(const std::string &playlist, unsigned pos) const +void Connection::Delete(const std::string &playlist, unsigned pos) { if (!itsConnection) return; - (isCommandsListEnabled ? mpd_send_playlist_delete : mpd_run_playlist_delete)(itsConnection, playlist.c_str(), pos); + if (!isCommandsListEnabled) + { + GoBusy(); + mpd_run_playlist_delete(itsConnection, playlist.c_str(), pos); + } + else + { + assert(!isIdle); + mpd_send_playlist_delete(itsConnection, playlist.c_str(), pos); + } } void Connection::StartCommandsList() { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_command_list_begin(itsConnection, 1); isCommandsListEnabled = 1; } @@ -575,33 +911,46 @@ bool Connection::CommitCommandsList() { if (!itsConnection) return false; + assert(isCommandsListEnabled); + assert(!isIdle); mpd_command_list_end(itsConnection); mpd_response_finish(itsConnection); - UpdateStatus(); if (GetPlaylistLength() == itsMaxPlaylistLength && itsErrorHandler) itsErrorHandler(this, MPD_SERVER_ERROR_PLAYLIST_MAX, Message::FullPlaylist, itsErrorHandlerUserdata); isCommandsListEnabled = 0; + UpdateStatus(); return !CheckForErrors(); } -bool Connection::DeletePlaylist(const std::string &name) const +bool Connection::DeletePlaylist(const std::string &name) { if (!itsConnection) return false; - return (isCommandsListEnabled ? mpd_send_rm : mpd_run_rm)(itsConnection, name.c_str()); + if (!isCommandsListEnabled) + { + GoBusy(); + return mpd_run_rm(itsConnection, name.c_str()); + } + else + { + assert(!isIdle); + return mpd_send_rm(itsConnection, name.c_str()); + } } -bool Connection::SavePlaylist(const std::string &name) const +bool Connection::SavePlaylist(const std::string &name) { if (!itsConnection) return false; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_save(itsConnection, name.c_str()); mpd_response_finish(itsConnection); return !(mpd_connection_get_error(itsConnection) == MPD_ERROR_SERVER && mpd_connection_get_server_error(itsConnection) == MPD_SERVER_ERROR_EXIST); } -void Connection::GetPlaylists(TagList &v) const +void Connection::GetPlaylists(TagList &v) { if (!itsConnection) return; @@ -613,10 +962,12 @@ void Connection::GetPlaylists(TagList &v) const FreeItemList(list); } -void Connection::GetList(TagList &v, mpd_tag_type type) const +void Connection::GetList(TagList &v, mpd_tag_type type) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_search_db_tags(itsConnection, type); mpd_search_commit(itsConnection); while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, type)) @@ -628,10 +979,12 @@ void Connection::GetList(TagList &v, mpd_tag_type type) const mpd_response_finish(itsConnection); } -void Connection::GetAlbums(const std::string &artist, TagList &v) const +void Connection::GetAlbums(const std::string &artist, TagList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_search_db_tags(itsConnection, MPD_TAG_ALBUM); if (!artist.empty()) mpd_search_add_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, MPD_TAG_ARTIST, artist.c_str()); @@ -645,7 +998,7 @@ void Connection::GetAlbums(const std::string &artist, TagList &v) const mpd_response_finish(itsConnection); } -void Connection::StartSearch(bool exact_match) const +void Connection::StartSearch(bool exact_match) { if (itsConnection) mpd_search_db_songs(itsConnection, exact_match); @@ -660,7 +1013,7 @@ void Connection::StartFieldSearch(mpd_tag_type item) } } -void Connection::AddSearch(mpd_tag_type item, const std::string &str) const +void Connection::AddSearch(mpd_tag_type item, const std::string &str) { // mpd version < 0.14.* doesn't support empty search constraints if (Version() < 14 && str.empty()) @@ -669,20 +1022,24 @@ void Connection::AddSearch(mpd_tag_type item, const std::string &str) const mpd_search_add_tag_constraint(itsConnection, MPD_OPERATOR_DEFAULT, item, str.c_str()); } -void Connection::CommitSearch(SongList &v) const +void Connection::CommitSearch(SongList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_search_commit(itsConnection); while (mpd_song *s = mpd_recv_song(itsConnection)) v.push_back(new Song(s)); mpd_response_finish(itsConnection); } -void Connection::CommitSearch(TagList &v) const +void Connection::CommitSearch(TagList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_search_commit(itsConnection); while (mpd_pair *tag = mpd_recv_pair_tag(itsConnection, itsSearchedField)) { @@ -693,10 +1050,12 @@ void Connection::CommitSearch(TagList &v) const mpd_response_finish(itsConnection); } -void Connection::GetDirectory(const std::string &path, ItemList &v) const +void Connection::GetDirectory(const std::string &path, ItemList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_list_meta(itsConnection, path.c_str()); while (mpd_entity *item = mpd_recv_entity(itsConnection)) { @@ -726,30 +1085,36 @@ void Connection::GetDirectory(const std::string &path, ItemList &v) const mpd_response_finish(itsConnection); } -void Connection::GetDirectoryRecursive(const std::string &path, SongList &v) const +void Connection::GetDirectoryRecursive(const std::string &path, SongList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_list_all_meta(itsConnection, path.c_str()); while (mpd_song *s = mpd_recv_song(itsConnection)) v.push_back(new Song(s)); mpd_response_finish(itsConnection); } -void Connection::GetSongs(const std::string &path, SongList &v) const +void Connection::GetSongs(const std::string &path, SongList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_list_meta(itsConnection, path.c_str()); while (mpd_song *s = mpd_recv_song(itsConnection)) v.push_back(new Song(s)); mpd_response_finish(itsConnection); } -void Connection::GetDirectories(const std::string &path, TagList &v) const +void Connection::GetDirectories(const std::string &path, TagList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_list_meta(itsConnection, path.c_str()); while (mpd_directory *dir = mpd_recv_directory(itsConnection)) { @@ -759,10 +1124,12 @@ void Connection::GetDirectories(const std::string &path, TagList &v) const mpd_response_finish(itsConnection); } -void Connection::GetOutputs(OutputList &v) const +void Connection::GetOutputs(OutputList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_outputs(itsConnection); while (mpd_output *output = mpd_recv_output(itsConnection)) { @@ -776,20 +1143,40 @@ bool Connection::EnableOutput(int id) { if (!itsConnection) return false; - return mpd_run_enable_output(itsConnection, id); + if (!isCommandsListEnabled) + { + GoBusy(); + return mpd_run_enable_output(itsConnection, id); + } + else + { + assert(!isIdle); + return mpd_send_enable_output(itsConnection, id); + } } bool Connection::DisableOutput(int id) { if (!itsConnection) return false; - return mpd_run_disable_output(itsConnection, id); + if (!isCommandsListEnabled) + { + GoBusy(); + return mpd_run_disable_output(itsConnection, id); + } + else + { + assert(!isIdle); + return mpd_send_disable_output(itsConnection, id); + } } -void Connection::GetURLHandlers(TagList &v) const +void Connection::GetURLHandlers(TagList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_list_url_schemes(itsConnection); while (mpd_pair *handler = mpd_recv_pair_named(itsConnection, "handler")) { @@ -799,10 +1186,12 @@ void Connection::GetURLHandlers(TagList &v) const mpd_response_finish(itsConnection); } -void Connection::GetTagTypes(TagList &v) const +void Connection::GetTagTypes(TagList &v) { if (!itsConnection) return; + assert(!isCommandsListEnabled); + GoBusy(); mpd_send_list_tag_types(itsConnection); while (mpd_pair *tag_type = mpd_recv_pair_named(itsConnection, "tagtype")) { diff --git a/src/mpdpp.h b/src/mpdpp.h index 99243309..72e4545b 100644 --- a/src/mpdpp.h +++ b/src/mpdpp.h @@ -21,6 +21,7 @@ #ifndef _MPDPP_H #define _MPDPP_H +#include #include #include @@ -92,12 +93,13 @@ namespace MPD int GetPort() { return itsPort; } float Version() const; + bool SupportsIdle() const { return supportsIdle; } void SetHostname(const std::string &); void SetPort(int port) { itsPort = port; } void SetTimeout(int timeout) { itsTimeout = timeout; } void SetPassword(const std::string &password) { itsPassword = password; } - bool SendPassword() const; + bool SendPassword(); void SetStatusUpdater(StatusUpdater, void *); void SetErrorHandler(ErrorHandler, void *); @@ -105,19 +107,19 @@ namespace MPD void UpdateStats(); bool UpdateDirectory(const std::string &); - void Play() const; - void Play(int) const; - void PlayID(int) const; - void Pause(bool) const; - void Toggle() const; - void Stop() const; - void Next() const; - void Prev() const; - void Move(unsigned, unsigned) const; - void Swap(unsigned, unsigned) const; - void Seek(unsigned) const; - void Shuffle() const; - void ClearPlaylist() const; + void Play(); + void Play(int); + void PlayID(int); + void Pause(bool); + void Toggle(); + void Stop(); + void Next(); + void Prev(); + void Move(unsigned, unsigned); + void Swap(unsigned, unsigned); + void Seek(unsigned); + void Shuffle(); + void ClearPlaylist(); bool isPlaying() const { return GetState() > psStop; } @@ -131,7 +133,7 @@ namespace MPD unsigned GetCrossfade() const { return itsCurrentStatus ? mpd_status_get_crossfade(itsCurrentStatus) : 0; } unsigned GetPlaylistID() const { return itsCurrentStatus ? mpd_status_get_queue_version(itsCurrentStatus) : 0; } unsigned GetOldPlaylistID() const { return itsOldStatus ? mpd_status_get_queue_version(itsOldStatus) : 0; } - unsigned GetElapsedTime() const { return itsCurrentStatus ? mpd_status_get_elapsed_time(itsCurrentStatus) : 0; } + unsigned GetElapsedTime() const { return itsCurrentStatus ? itsElapsed : 0; } int GetTotalTime() const { return itsCurrentStatus ? mpd_status_get_total_time(itsCurrentStatus) : 0; } unsigned GetBitrate() const { return itsCurrentStatus ? mpd_status_get_kbit_rate(itsCurrentStatus) : 0; } @@ -145,66 +147,69 @@ namespace MPD size_t GetMaxPlaylistLength() const { return itsMaxPlaylistLength; } size_t GetPlaylistLength() const { return itsCurrentStatus ? mpd_status_get_queue_length(itsCurrentStatus) : 0; } - void GetPlaylistChanges(unsigned, SongList &) const; + void GetPlaylistChanges(unsigned, SongList &); const std::string & GetErrorMessage() const { return itsErrorMessage; } int GetErrorCode() const { return itsErrorCode; } - Song GetCurrentSong() const; + Song GetCurrentSong(); int GetCurrentSongPos() const; - Song GetSong(const std::string &) const; - void GetPlaylistContent(const std::string &, SongList &) const; - - void SetRepeat(bool) const; - void SetRandom(bool) const; - void SetSingle(bool) const; - void SetConsume(bool) const; - void SetCrossfade(unsigned) const; + Song GetSong(const std::string &); + void GetPlaylistContent(const std::string &, SongList &); + + void SetRepeat(bool); + void SetRandom(bool); + void SetSingle(bool); + void SetConsume(bool); + void SetCrossfade(unsigned); void SetVolume(unsigned); - std::string GetReplayGainMode() const; - void SetReplayGainMode(ReplayGainMode) const; + std::string GetReplayGainMode(); + void SetReplayGainMode(ReplayGainMode); int AddSong(const std::string &, int = -1); // returns id of added song int AddSong(const Song &, int = -1); // returns id of added song bool AddRandomSongs(size_t); - void Add(const std::string &path) const; - void Delete(unsigned) const; - void DeleteID(unsigned) const; - void Delete(const std::string &, unsigned) const; + void Add(const std::string &path); + void Delete(unsigned); + void DeleteID(unsigned); + void Delete(const std::string &, unsigned); void StartCommandsList(); bool CommitCommandsList(); - bool DeletePlaylist(const std::string &) const; - bool SavePlaylist(const std::string &) const; - void ClearPlaylist(const std::string &) const; - void AddToPlaylist(const std::string &, const Song &) const; - void AddToPlaylist(const std::string &, const std::string &) const; - void Move(const std::string &, int, int) const; - bool Rename(const std::string &, const std::string &) const; + bool DeletePlaylist(const std::string &); + bool SavePlaylist(const std::string &); + void ClearPlaylist(const std::string &); + void AddToPlaylist(const std::string &, const Song &); + void AddToPlaylist(const std::string &, const std::string &); + void Move(const std::string &, int, int); + bool Rename(const std::string &, const std::string &); - void StartSearch(bool) const; + void StartSearch(bool); void StartFieldSearch(mpd_tag_type); - void AddSearch(mpd_tag_type, const std::string &) const; - void CommitSearch(SongList &) const; - void CommitSearch(TagList &) const; - - void GetPlaylists(TagList &) const; - void GetList(TagList &, mpd_tag_type) const; - void GetAlbums(const std::string &, TagList &) const; - void GetDirectory(const std::string &, ItemList &) const; - void GetDirectoryRecursive(const std::string &, SongList &) const; - void GetSongs(const std::string &, SongList &) const; - void GetDirectories(const std::string &, TagList &) const; - - void GetOutputs(OutputList &) const; + void AddSearch(mpd_tag_type, const std::string &); + void CommitSearch(SongList &); + void CommitSearch(TagList &); + + void GetPlaylists(TagList &); + void GetList(TagList &, mpd_tag_type); + void GetAlbums(const std::string &, TagList &); + void GetDirectory(const std::string &, ItemList &); + void GetDirectoryRecursive(const std::string &, SongList &); + void GetSongs(const std::string &, SongList &); + void GetDirectories(const std::string &, TagList &); + + void GetOutputs(OutputList &); bool EnableOutput(int); bool DisableOutput(int); - void GetURLHandlers(TagList &v) const; - void GetTagTypes(TagList &v) const; + void GetURLHandlers(TagList &v); + void GetTagTypes(TagList &v); private: + void GoIdle(); + mpd_idle GoBusy(); + int CheckForErrors(); mpd_connection *itsConnection; @@ -214,6 +219,10 @@ namespace MPD int itsErrorCode; size_t itsMaxPlaylistLength; + pollfd itsPoll; + bool isIdle; + bool supportsIdle; + std::string itsHost; int itsPort; int itsTimeout; @@ -223,6 +232,9 @@ namespace MPD mpd_status *itsOldStatus; mpd_stats *itsStats; + unsigned itsElapsed; + time_t itsElapsedTimer[2]; + StatusChanges itsChanges; StatusUpdater itsUpdater; diff --git a/src/ncmpcpp.h b/src/ncmpcpp.h index dca899fc..affd75f0 100644 --- a/src/ncmpcpp.h +++ b/src/ncmpcpp.h @@ -37,7 +37,7 @@ using namespace NCurses; typedef std::pair string_pair; -const int ncmpcpp_window_timeout = 250; +const int ncmpcpp_window_timeout = 100; #endif diff --git a/src/status.cpp b/src/status.cpp index 3f073a00..788ea1d7 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -53,8 +53,6 @@ namespace bool block_statusbar_update = 0; bool block_progressbar_update = 0; bool allow_statusbar_unlock = 1; - - int local_elapsed; } #ifndef USE_PDCURSES @@ -122,7 +120,7 @@ void TraceMpdStatus() static timeval past, now; gettimeofday(&now, 0); - if ((Mpd.Connected() && now.tv_sec > past.tv_sec) || UpdateStatusImmediately) + if (Mpd.Connected() && (Mpd.SupportsIdle() || now.tv_sec > past.tv_sec || UpdateStatusImmediately)) { Mpd.UpdateStatus(); BlockItemListUpdate = 0; @@ -154,7 +152,7 @@ void TraceMpdStatus() if (Mpd.GetState() != psPlay && !block_statusbar_update && !block_progressbar_update) { if (Config.new_design) - DrawProgressbar(local_elapsed, Mpd.GetTotalTime()); + DrawProgressbar(Mpd.GetElapsedTime(), Mpd.GetTotalTime()); else Statusbar() << wclrtoeol; wFooter->Refresh(); @@ -167,12 +165,21 @@ void NcmpcppErrorCallback(Connection *, int errorid, const char *msg, void *) { if (errorid == MPD_SERVER_ERROR_PERMISSION) { - wFooter->SetGetStringHelper(0); - Statusbar() << "Password: "; - Mpd.SetPassword(wFooter->GetString(-1, 0, 1)); - Mpd.SendPassword(); - Mpd.UpdateStatus(); - wFooter->SetGetStringHelper(StatusbarGetStringHelper); + Statusbar() << msg << ", enter password ? [" << fmtBold << 'y' << fmtBoldEnd << "/" << fmtBold << 'n' << fmtBoldEnd << "]"; + wFooter->Refresh(); + int answer = 0; + do + wFooter->ReadKey(answer); + while (answer != 'y' && answer != 'n'); + if (answer == 'y') + { + wFooter->SetGetStringHelper(0); + Statusbar() << "Password: "; + Mpd.SetPassword(wFooter->GetString(-1, 0, 1)); + if (Mpd.SendPassword()) + ShowMessage("Password accepted!"); + wFooter->SetGetStringHelper(StatusbarGetStringHelper); + } } else ShowMessage("%s", msg); @@ -310,6 +317,7 @@ void NcmpcppStatusChanged(Connection *, StatusChanges changed, void *) case psPause: { player_state = Config.new_design ? "[paused] " : "[Paused] "; + changed.ElapsedTime = 1; break; } case psStop: @@ -364,38 +372,26 @@ void NcmpcppStatusChanged(Connection *, StatusChanges changed, void *) Lyrics::Reload = 1; } Playlist::ReloadRemaining = 1; - playing_song_scroll_begin = 0; first_line_scroll_begin = 0; second_line_scroll_begin = 0; } - static time_t now, past = 0; - time(&now); - if (((now > past || changed.SongID) && Mpd.isPlaying()) || RedrawStatusbar) + if (changed.ElapsedTime || changed.SongID || RedrawStatusbar) { - time(&past); if (np.Empty() && !(np = Mpd.GetCurrentSong()).Empty()) WindowTitle(utf_to_locale_cpy(np.toString(Config.song_window_title_format))); if (!np.Empty() && Mpd.isPlaying()) { - changed.ElapsedTime = 1; - - int mpd_elapsed = Mpd.GetElapsedTime(); - if (local_elapsed < mpd_elapsed-2 || local_elapsed+1 > mpd_elapsed) - local_elapsed = mpd_elapsed; - else if (Mpd.GetState() == psPlay && !RedrawStatusbar) - ++local_elapsed; - std::string tracklength; if (Config.new_design) { if (Config.display_remaining_time) { tracklength = "-"; - tracklength += Song::ShowTime(Mpd.GetTotalTime()-local_elapsed); + tracklength += Song::ShowTime(Mpd.GetTotalTime()-Mpd.GetElapsedTime()); } else - tracklength = Song::ShowTime(local_elapsed); + tracklength = Song::ShowTime(Mpd.GetElapsedTime()); if (Mpd.GetTotalTime()) { tracklength += "/"; @@ -448,17 +444,17 @@ void NcmpcppStatusChanged(Connection *, StatusChanges changed, void *) if (Config.display_remaining_time) { tracklength += "-"; - tracklength += Song::ShowTime(Mpd.GetTotalTime()-local_elapsed); + tracklength += Song::ShowTime(Mpd.GetTotalTime()-Mpd.GetElapsedTime()); } else - tracklength += Song::ShowTime(local_elapsed); + tracklength += Song::ShowTime(Mpd.GetElapsedTime()); tracklength += "/"; tracklength += MPD::Song::ShowTime(Mpd.GetTotalTime()); tracklength += "]"; } else { - tracklength += Song::ShowTime(local_elapsed); + tracklength += Song::ShowTime(Mpd.GetElapsedTime()); tracklength += "]"; } basic_buffer np_song; @@ -468,7 +464,7 @@ void NcmpcppStatusChanged(Connection *, StatusChanges changed, void *) *wFooter << fmtBold << XY(wFooter->GetWidth()-tracklength.length(), 1) << tracklength; } if (!block_progressbar_update) - DrawProgressbar(local_elapsed, Mpd.GetTotalTime()); + DrawProgressbar(Mpd.GetElapsedTime(), Mpd.GetTotalTime()); RedrawStatusbar = 0; } else