/*************************************************************************** * Copyright (C) 2008-2014 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 #include #include #include #include #include #include #include #include "mpdpp.h" #include "actions.h" #include "bindings.h" #include "browser.h" #include "charset.h" #include "configuration.h" #include "global.h" #include "helpers.h" #include "lyrics.h" #include "outputs.h" #include "playlist.h" #include "settings.h" #include "status.h" #include "statusbar.h" #include "visualizer.h" #include "title.h" #include "utility/conversion.h" namespace ph = std::placeholders; namespace { std::ofstream errorlog; std::streambuf *cerr_buffer; bool run_resize_screen = false; void sighandler(int sig) { if (sig == SIGWINCH) { run_resize_screen = true; } # if defined(__sun) && defined(__SVR4) // in solaris it is needed to reinstall the handler each time it's executed signal(sig, sighandler); # endif // __sun && __SVR4 } void do_at_exit() { // restore old cerr buffer std::cerr.rdbuf(cerr_buffer); errorlog.close(); Mpd.Disconnect(); NC::destroyScreen(); windowTitle(""); } } int main(int argc, char **argv) { using Global::myScreen; using Global::wHeader; using Global::wFooter; using Global::VolumeState; using Global::Timer; std::setlocale(LC_ALL, ""); std::locale::global(Charset::internalLocale()); if (!configure(argc, argv)) return 0; // always execute these commands, even if ncmpcpp use exit function atexit(do_at_exit); // redirect std::cerr output to ~/.ncmpcpp/error.log file errorlog.open((Config.ncmpcpp_directory + "error.log").c_str(), std::ios::app); cerr_buffer = std::cerr.rdbuf(); std::cerr.rdbuf(errorlog.rdbuf()); # ifndef WIN32 signal(SIGPIPE, sighandler); signal(SIGWINCH, sighandler); // ignore Ctrl-C sigignore(SIGINT); # endif // !WIN32 NC::initScreen(Config.colors_enabled, Config.mouse_support); Actions::OriginalStatusbarVisibility = Config.statusbar_visibility; if (Config.design == Design::Alternative) Config.statusbar_visibility = 0; Actions::setWindowsDimensions(); Actions::validateScreenSize(); Actions::initializeScreens(); wHeader = new NC::Window(0, 0, COLS, Actions::HeaderHeight, "", Config.header_color, NC::Border()); if (Config.header_visibility || Config.design == Design::Alternative) wHeader->display(); wFooter = new NC::Window(0, Actions::FooterStartY, COLS, Actions::FooterHeight, "", Config.statusbar_color, NC::Border()); wFooter->setPromptHook(Statusbar::Helpers::mainHook); // initialize global timer Timer = boost::posix_time::microsec_clock::local_time(); // initialize global random number generator Global::RNG.seed(std::random_device()()); // initialize playlist myPlaylist->switchTo(); // go to startup screen if (Config.startup_screen_type != myScreen->type()) { auto startup_screen = toScreen(Config.startup_screen_type); assert(startup_screen != nullptr); startup_screen->switchTo(); } // lock current screen and go to the slave one if applicable if (Config.startup_slave_screen_type) { auto slave_screen_type = *Config.startup_slave_screen_type; bool screen_locked = myScreen->lock(); if (screen_locked && slave_screen_type != myScreen->type()) { auto slave_screen = toScreen(slave_screen_type); assert(slave_screen != nullptr); slave_screen->switchTo(); if (!Config.startup_slave_screen_focus) Actions::get(Actions::Type::MasterScreen).execute(); } } // local variables bool key_pressed = false; auto input = NC::Key::None; auto connect_attempt = boost::posix_time::from_time_t(0); auto update_environment = static_cast( Actions::get(Actions::Type::UpdateEnvironment) ); while (!Actions::ExitMainLoop) { try { if (!Mpd.Connected() && Timer - connect_attempt > boost::posix_time::seconds(1)) { connect_attempt = Timer; // reset local status info Status::clear(); // clear mpd callback wFooter->clearFDCallbacksList(); try { Mpd.Connect(); if (Mpd.Version() < 16) { Mpd.Disconnect(); throw MPD::ClientError(MPD_ERROR_STATE, "MPD < 0.16.0 is not supported", false); } } catch (MPD::ClientError &e) { Status::handleClientError(e); } } if (run_resize_screen) { Actions::resizeScreen(true); run_resize_screen = false; } update_environment.run(!key_pressed, key_pressed); input = readKey(*wFooter); key_pressed = input != NC::Key::None; if (!key_pressed) continue; //Statusbar::print(ToString(keyToWString(input))); // The reason we want to update timer here is that if the timer is updated // in Status::trace, then Key::read usually blocks for 500ms and if key is // pressed 400ms after Key::read was called, we end up with Timer that is // ~400ms inaccurate. On the other hand, if keys are being pressed, we don't // want to update timer in both Status::trace and here. Therefore we update // timer in Status::trace only if there was no recent input. Timer = boost::posix_time::microsec_clock::local_time(); try { auto k = Bindings.get(input); std::any_of(k.first, k.second, std::bind(&Binding::execute, ph::_1)); } catch (ConversionError &e) { Statusbar::printf("Invalid value: %1%", e.value()); } catch (OutOfBounds &e) { Statusbar::printf("Error: %1%", e.errorMessage()); } catch (NC::PromptAborted &) { Statusbar::printf("Action aborted"); } if (myScreen == myPlaylist) myPlaylist->enableHighlighting(); } catch (MPD::ClientError &e) { Status::handleClientError(e); } catch (MPD::ServerError &e) { Status::handleServerError(e); } catch (std::exception &e) { Statusbar::printf("Unexpected error: %1%", e.what()); } } return 0; }