diff --git a/doc/keys b/doc/keys index 338f1045..395fc0de 100644 --- a/doc/keys +++ b/doc/keys @@ -62,6 +62,8 @@ # #key_clock = '0' 274 # +#key_server_info = '@' +# #key_stop = 's' # #key_pause = 'P' diff --git a/src/Makefile.am b/src/Makefile.am index e5293b27..cc510b1b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,8 +2,8 @@ bin_PROGRAMS = ncmpcpp ncmpcpp_SOURCES = browser.cpp charset.cpp clock.cpp conv.cpp display.cpp \ error.cpp help.cpp helpers.cpp info.cpp lyrics.cpp media_library.cpp menu.cpp \ misc.cpp mpdpp.cpp ncmpcpp.cpp outputs.cpp playlist.cpp playlist_editor.cpp \ - scrollpad.cpp search_engine.cpp settings.cpp song.cpp status.cpp tag_editor.cpp \ - tiny_tag_editor.cpp visualizer.cpp window.cpp + scrollpad.cpp search_engine.cpp server_info.cpp settings.cpp song.cpp status.cpp \ + tag_editor.cpp tiny_tag_editor.cpp visualizer.cpp window.cpp # set the include path found by configure INCLUDES= $(all_includes) @@ -11,6 +11,7 @@ INCLUDES= $(all_includes) # the library search path. ncmpcpp_LDFLAGS = $(all_libraries) noinst_HEADERS = browser.h charset.h clock.h conv.h display.h error.h global.h \ - help.h helpers.h home.h info.h lyrics.h media_library.h menu.h misc.h mpdpp.h \ - outputs.h playlist_editor.h screen.h scrollpad.h search_engine.h settings.h \ - song.h tag_editor.h tiny_tag_editor.h visualizer.h window.h + help.h helpers.h home.h info.h lyrics.h media_library.h menu.h misc.h \ + mpdpp.h outputs.h playlist_editor.h screen.h scrollpad.h search_engine.h \ + server_info.h settings.h song.h tag_editor.h tiny_tag_editor.h visualizer.h \ + window.h diff --git a/src/help.cpp b/src/help.cpp index 5cef44cc..66601098 100644 --- a/src/help.cpp +++ b/src/help.cpp @@ -153,7 +153,8 @@ void Help::GetKeybindings() # ifdef ENABLE_CLOCK *w << DisplayKeys(Key.Clock) << "Clock screen\n"; # endif // ENABLE_CLOCK - + *w << "\n"; + *w << DisplayKeys(Key.ServerInfo) << "MPD server info\n"; *w << "\n\n " << fmtBold << "Keys - Global\n -----------------------------------------\n" << fmtBoldEnd; *w << DisplayKeys(Key.Stop) << "Stop\n"; diff --git a/src/helpers.cpp b/src/helpers.cpp index b0242085..c1479efc 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -244,6 +244,14 @@ bool CaseInsensitiveSorting::operator()(const Item &a, const Item &b) return a.type < b.type; } +std::string Timestamp(time_t t) +{ + char result[32]; + tm info; + result[strftime(result, 31, "%x %X", localtime_r(&t, &info))] = 0; + return result; +} + void UpdateSongList(Menu *menu) { bool bold = 0; diff --git a/src/helpers.h b/src/helpers.h index e44e375c..7dee4110 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -162,6 +162,49 @@ template void String2Buffer(const std::basic_string &s, basic_bu } } +template void ShowTime(T &buf, size_t length, bool short_names) +{ + const unsigned MINUTE = 60; + const unsigned HOUR = 60*MINUTE; + const unsigned DAY = 24*HOUR; + const unsigned YEAR = 365*DAY; + + unsigned years = length/YEAR; + if (years) + { + buf << years << (short_names ? "y" : (years == 1 ? " year" : " years")); + length -= years*YEAR; + if (length) + buf << ", "; + } + unsigned days = length/DAY; + if (days) + { + buf << days << (short_names ? "d" : (days == 1 ? " day" : " days")); + length -= days*DAY; + if (length) + buf << ", "; + } + unsigned hours = length/HOUR; + if (hours) + { + buf << hours << (short_names ? "h" : (hours == 1 ? " hour" : " hours")); + length -= hours*HOUR; + if (length) + buf << ", "; + } + unsigned minutes = length/MINUTE; + if (minutes) + { + buf << minutes << (short_names ? "m" : (minutes == 1 ? " minute" : " minutes")); + length -= minutes*MINUTE; + if (length) + buf << ", "; + } + if (length) + buf << length << (short_names ? "s" : (length == 1 ? " second" : " seconds")); +} + template void ShowTag(T &buf, const std::string &tag) { if (tag.empty()) @@ -175,6 +218,8 @@ inline bool Keypressed(int in, const int *key) return in == key[0] || in == key[1]; } +std::string Timestamp(time_t t); + void UpdateSongList(Menu *); #ifdef HAVE_TAGLIB_H diff --git a/src/mpdpp.cpp b/src/mpdpp.cpp index 2bae7a42..90c6aa26 100644 --- a/src/mpdpp.cpp +++ b/src/mpdpp.cpp @@ -41,6 +41,7 @@ Connection::Connection() : itsConnection(0), itsTimeout(15), itsCurrentStatus(0), itsOldStatus(0), + itsStats(0), itsUpdater(0), itsErrorHandler(0) { @@ -50,6 +51,8 @@ Connection::~Connection() { if (itsConnection) mpd_connection_free(itsConnection); + if (itsStats) + mpd_stats_free(itsStats); if (itsOldStatus) mpd_status_free(itsOldStatus); if (itsCurrentStatus) @@ -77,6 +80,8 @@ void Connection::Disconnect() { if (itsConnection) mpd_connection_free(itsConnection); + if (itsStats) + mpd_stats_free(itsStats); if (itsOldStatus) mpd_status_free(itsOldStatus); if (itsCurrentStatus) @@ -84,6 +89,7 @@ void Connection::Disconnect() itsConnection = 0; itsCurrentStatus = 0; itsOldStatus = 0; + itsStats = 0; isCommandsListEnabled = 0; itsMaxPlaylistLength = -1; } @@ -187,6 +193,15 @@ void Connection::UpdateStatus() } } +void Connection::UpdateStats() +{ + if (!itsConnection) + return; + if (itsStats) + mpd_stats_free(itsStats); + itsStats = mpd_run_stats(itsConnection); +} + bool Connection::UpdateDirectory(const std::string &path) { if (!itsConnection) @@ -729,6 +744,32 @@ bool Connection::DisableOutput(int id) return mpd_run_disable_output(itsConnection, id); } +void Connection::GetURLHandlers(TagList &v) const +{ + if (!itsConnection) + return; + mpd_send_list_url_schemes(itsConnection); + while (mpd_pair *handler = mpd_recv_pair_named(itsConnection, "handler")) + { + v.push_back(handler->value); + mpd_return_pair(itsConnection, handler); + } + mpd_response_finish(itsConnection); +} + +void Connection::GetTagTypes(TagList &v) const +{ + if (!itsConnection) + return; + mpd_send_list_tag_types(itsConnection); + while (mpd_pair *tag_type = mpd_recv_pair_named(itsConnection, "tagtype")) + { + v.push_back(tag_type->value); + mpd_return_pair(itsConnection, tag_type); + } + mpd_response_finish(itsConnection); +} + int Connection::CheckForErrors() { if ((itsErrorCode = mpd_connection_get_error(itsConnection)) != MPD_ERROR_SUCCESS) diff --git a/src/mpdpp.h b/src/mpdpp.h index c4b875c2..69354315 100644 --- a/src/mpdpp.h +++ b/src/mpdpp.h @@ -101,6 +101,7 @@ namespace MPD void SetStatusUpdater(StatusUpdater, void *); void SetErrorHandler(ErrorHandler, void *); void UpdateStatus(); + void UpdateStats(); bool UpdateDirectory(const std::string &); void Play() const; @@ -133,6 +134,14 @@ namespace MPD int GetTotalTime() const { return itsCurrentStatus ? mpd_status_get_total_time(itsCurrentStatus) : 0; } unsigned GetBitrate() const { return itsCurrentStatus ? mpd_status_get_kbit_rate(itsCurrentStatus) : 0; } + unsigned NumberOfArtists() const { return itsStats ? mpd_stats_get_number_of_artists(itsStats) : 0; } + unsigned NumberOfAlbums() const { return itsStats ? mpd_stats_get_number_of_albums(itsStats) : 0; } + unsigned NumberOfSongs() const { return itsStats ? mpd_stats_get_number_of_songs(itsStats) : 0; } + unsigned long Uptime() const { return itsStats ? mpd_stats_get_uptime(itsStats) : 0; } + unsigned long DBUpdateTime() const { return itsStats ? mpd_stats_get_db_update_time(itsStats) : 0; } + unsigned long PlayTime() const { return itsStats ? mpd_stats_get_play_time(itsStats) : 0; } + unsigned long DBPlayTime() const { return itsStats ? mpd_stats_get_db_play_time(itsStats) : 0; } + size_t GetMaxPlaylistLength() const { return itsMaxPlaylistLength; } size_t GetPlaylistLength() const { return itsCurrentStatus ? mpd_status_get_queue_length(itsCurrentStatus) : 0; } void GetPlaylistChanges(unsigned, SongList &) const; @@ -188,6 +197,9 @@ namespace MPD bool EnableOutput(int); bool DisableOutput(int); + void GetURLHandlers(TagList &v) const; + void GetTagTypes(TagList &v) const; + private: int CheckForErrors(); @@ -205,6 +217,7 @@ namespace MPD mpd_status *itsCurrentStatus; mpd_status *itsOldStatus; + mpd_stats *itsStats; StatusChanges itsChanges; diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index 172c6786..c36108dd 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -40,6 +40,7 @@ #include "helpers.h" #include "media_library.h" #include "misc.h" +#include "server_info.h" #include "lyrics.h" #include "playlist.h" #include "playlist_editor.h" @@ -1933,6 +1934,10 @@ int main(int argc, char *argv[]) myClock->SwitchTo(); } # endif // ENABLE_CLOCK + else if (Keypressed(input, Key.ServerInfo)) + { + myServerInfo->SwitchTo(); + } // key mapping end } return 0; diff --git a/src/playlist.cpp b/src/playlist.cpp index 257f54c3..2d5f83a5 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -361,60 +361,17 @@ std::string Playlist::TotalLength() if (itsTotalLength) { result << ", length: "; - ShowTime(result, itsTotalLength); + ShowTime(result, itsTotalLength, 0); } if (Config.playlist_show_remaining_time && itsRemainingTime && !Items->isFiltered() && Items->Size() > 1) { result << " :: remaining: "; - ShowTime(result, itsRemainingTime); + ShowTime(result, itsRemainingTime, 0); } result << ')'; return result.str(); } -void Playlist::ShowTime(std::ostringstream &result, size_t length) -{ - const int MINUTE = 60; - const int HOUR = 60*MINUTE; - const int DAY = 24*HOUR; - const int YEAR = 365*DAY; - - int years = length/YEAR; - if (years) - { - result << years << (years == 1 ? " year" : " years"); - length -= years*YEAR; - if (length) - result << ", "; - } - int days = length/DAY; - if (days) - { - result << days << (days == 1 ? " day" : " days"); - length -= days*DAY; - if (length) - result << ", "; - } - int hours = length/HOUR; - if (hours) - { - result << hours << (hours == 1 ? " hour" : " hours"); - length -= hours*HOUR; - if (length) - result << ", "; - } - int minutes = length/MINUTE; - if (minutes) - { - result << minutes << (minutes == 1 ? " minute" : " minutes"); - length -= minutes*MINUTE; - if (length) - result << ", "; - } - if (length) - result << length << (length == 1 ? " second" : " seconds"); -} - const MPD::Song *Playlist::NowPlayingSong() { bool was_filtered = Items->isFiltered(); diff --git a/src/playlist.h b/src/playlist.h index a2c17451..79f21d4a 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -94,7 +94,6 @@ class Playlist : public Screen time_t itsTimer; - static void ShowTime(std::ostringstream &, size_t); static bool Sorting(MPD::Song *a, MPD::Song *b); static Menu< std::pair > *SortDialog; diff --git a/src/server_info.cpp b/src/server_info.cpp new file mode 100644 index 00000000..cfad98f4 --- /dev/null +++ b/src/server_info.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (C) 2008-2009 by Andrzej Rybczak * + * electricityispower@gmail.com * + * * + * 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., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include + +#include "global.h" +#include "server_info.h" + +using Global::MainHeight; +using Global::MainStartY; +using Global::myScreen; +using Global::myOldScreen; + +ServerInfo *myServerInfo = new ServerInfo; + +void ServerInfo::Init() +{ + SetDimensions(); + w = new Scrollpad((COLS-itsWidth)/2, (MainHeight-itsHeight)/2+MainStartY, itsWidth, itsHeight, "MPD server info", Config.main_color, Config.window_border); + w->SetTimeout(ncmpcpp_window_timeout); + + Mpd.GetURLHandlers(itsURLHandlers); + Mpd.GetTagTypes(itsTagTypes); + + isInitialized = 1; +} + +void ServerInfo::SwitchTo() +{ + if (myScreen == this) + { + myOldScreen->SwitchTo(); + return; + } + if (MainHeight < 5) + { + ShowMessage("Screen is too small to display this window!"); + return; + } + + if (!isInitialized) + Init(); + + // Resize() can fall back to old screen, so we need it updated + myOldScreen = myScreen; + + if (hasToBeResized) + Resize(); + + myScreen = this; + w->Window::Clear(); +} + +void ServerInfo::Resize() +{ + SetDimensions(); + if (itsHeight < 5) // screen too low to display this window + return myOldScreen->SwitchTo(); + w->Resize(itsWidth, itsHeight); + w->MoveTo((COLS-itsWidth)/2, (MainHeight-itsHeight)/2+MainStartY); + if (myOldScreen && myOldScreen->hasToBeResized) // resize background window + { + myOldScreen->Resize(); + myOldScreen->Refresh(); + } + hasToBeResized = 0; +} + +std::basic_string ServerInfo::Title() +{ + return myOldScreen->Title(); +} + +void ServerInfo::Update() +{ + static time_t now = 0, past; + time(&past); + if (past <= now) + return; + time(&now); + + Mpd.UpdateStats(); + w->Clear(0); + + *w << fmtBold << U("Version: ") << fmtBoldEnd << U("0.") << std::fixed << std::setprecision(1) << Mpd.Version() << '\n'; + *w << fmtBold << U("Uptime: ") << fmtBoldEnd; + ShowTime(*w, Mpd.Uptime(), 1); + *w << '\n'; + *w << fmtBold << U("Time playing: ") << fmtBoldEnd << MPD::Song::ShowTime(Mpd.PlayTime()) << '\n'; + *w << '\n'; + *w << fmtBold << U("Total playtime: ") << fmtBoldEnd; + ShowTime(*w, Mpd.DBPlayTime(), 1); + *w << '\n'; + *w << fmtBold << U("Number of artists: ") << fmtBoldEnd << Mpd.NumberOfArtists() << '\n'; + *w << fmtBold << U("Number of albums: ") << fmtBoldEnd << Mpd.NumberOfAlbums() << '\n'; + *w << fmtBold << U("Number of songs: ") << fmtBoldEnd << Mpd.NumberOfSongs() << '\n'; + *w << '\n'; + *w << fmtBold << U("Last DB update: ") << fmtBoldEnd << Timestamp(Mpd.DBUpdateTime()) << '\n'; + *w << '\n'; + *w << fmtBold << U("URL Handlers:") << fmtBoldEnd; + for (MPD::TagList::const_iterator it = itsURLHandlers.begin(); it != itsURLHandlers.end(); ++it) + *w << (it != itsURLHandlers.begin() ? U(", ") : U(" ")) << *it; + *w << U("\n\n"); + *w << fmtBold << U("Tag Types:") << fmtBoldEnd; + for (MPD::TagList::const_iterator it = itsTagTypes.begin(); it != itsTagTypes.end(); ++it) + *w << (it != itsTagTypes.begin() ? U(", ") : U(" ")) << *it; + + w->Flush(); + w->Refresh(); +} + +void ServerInfo::SetDimensions() +{ + itsWidth = COLS*0.6; + itsHeight = std::min(size_t(LINES*0.7), MainHeight); +} + diff --git a/src/server_info.h b/src/server_info.h new file mode 100644 index 00000000..dc668a53 --- /dev/null +++ b/src/server_info.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2008-2009 by Andrzej Rybczak * + * electricityispower@gmail.com * + * * + * 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., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef _SERVER_INFO +#define _SERVER_INFO + +#include "screen.h" + +class ServerInfo : public Screen +{ + public: + virtual void SwitchTo(); + virtual void Resize(); + + virtual std::basic_string Title(); + + virtual void Update(); + + virtual void EnterPressed() { } + virtual void SpacePressed() { } + + virtual bool allowsSelection() { return false; } + + virtual List *GetList() { return 0; } + + protected: + virtual void Init(); + + private: + void SetDimensions(); + + MPD::TagList itsURLHandlers; + MPD::TagList itsTagTypes; + + size_t itsWidth; + size_t itsHeight; +}; + +extern ServerInfo *myServerInfo; + +#endif + diff --git a/src/settings.cpp b/src/settings.cpp index 6bd9c7a8..42053f5c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -123,6 +123,7 @@ void DefaultKeys(ncmpcpp_keys &keys) keys.Outputs[0] = '8'; keys.Visualizer[0] = '9'; keys.Clock[0] = '0'; + keys.ServerInfo[0] = '@'; keys.Stop[0] = 's'; keys.Pause[0] = 'P'; keys.Next[0] = '>'; @@ -194,6 +195,7 @@ void DefaultKeys(ncmpcpp_keys &keys) keys.Outputs[1] = 272; keys.Visualizer[1] = 273; keys.Clock[1] = 274; + keys.ServerInfo[1] = null_key; keys.Stop[1] = null_key; keys.Pause[1] = null_key; keys.Next[1] = null_key; @@ -388,6 +390,8 @@ void ReadKeys(ncmpcpp_keys &keys) GetKeys(key, keys.Visualizer); else if (key.find("key_clock ") != std::string::npos) GetKeys(key, keys.Clock); + else if (key.find("key_server_info ") != std::string::npos) + GetKeys(key, keys.ServerInfo); else if (key.find("key_stop ") != std::string::npos) GetKeys(key, keys.Stop); else if (key.find("key_pause ") != std::string::npos) diff --git a/src/settings.h b/src/settings.h index 63f5800b..b0f6da1e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -74,6 +74,7 @@ struct ncmpcpp_keys int Outputs[2]; int Visualizer[2]; int Clock[2]; + int ServerInfo[2]; int Stop[2]; int Pause[2]; int Next[2];