status: store status fields seperately
[ncmpcpp.git] / src / ncmpcpp.cpp
blobe3da219b9def25d1389b5b8c553597db9794080e
1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include <cerrno>
22 #include <clocale>
23 #include <csignal>
24 #include <cstring>
26 #include <boost/date_time/posix_time/posix_time.hpp>
27 #include <boost/locale.hpp>
28 #include <iostream>
29 #include <fstream>
30 #include <stdexcept>
32 #include "mpdpp.h"
34 #include "actions.h"
35 #include "bindings.h"
36 #include "browser.h"
37 #include "charset.h"
38 #include "configuration.h"
39 #include "global.h"
40 #include "error.h"
41 #include "helpers.h"
42 #include "lyrics.h"
43 #include "outputs.h"
44 #include "playlist.h"
45 #include "settings.h"
46 #include "status.h"
47 #include "statusbar.h"
48 #include "visualizer.h"
49 #include "title.h"
50 #include "utility/conversion.h"
52 namespace
54 std::ofstream errorlog;
55 std::streambuf *cerr_buffer;
56 bool run_resize_screen = false;
58 # if !defined(WIN32)
59 void sighandler(int sig)
61 if (sig == SIGPIPE)
63 Statusbar::print("SIGPIPE (broken pipe signal) received");
65 else if (sig == SIGWINCH)
67 run_resize_screen = true;
69 # if defined(__sun) && defined(__SVR4)
70 // in solaris it is needed to reinstall the handler each time it's executed
71 signal(sig, sighandler);
72 # endif // __sun && __SVR4
74 # endif // !WIN32
76 void do_at_exit()
78 // restore old cerr buffer
79 std::cerr.rdbuf(cerr_buffer);
80 errorlog.close();
81 Mpd.Disconnect();
82 # ifndef USE_PDCURSES // destroying screen somehow crashes pdcurses
83 NC::destroyScreen();
84 # endif // USE_PDCURSES
85 windowTitle("");
89 int main(int argc, char **argv)
91 using Global::myScreen;
93 using Global::wHeader;
94 using Global::wFooter;
96 using Global::VolumeState;
97 using Global::Timer;
99 srand(time(nullptr));
100 std::setlocale(LC_ALL, "");
101 std::locale::global(Charset::internalLocale());
103 if (!configure(argc, argv))
104 return 0;
106 // always execute these commands, even if ncmpcpp use exit function
107 atexit(do_at_exit);
109 // redirect std::cerr output to ~/.ncmpcpp/error.log file
110 errorlog.open((Config.ncmpcpp_directory + "error.log").c_str(), std::ios::app);
111 cerr_buffer = std::cerr.rdbuf();
112 std::cerr.rdbuf(errorlog.rdbuf());
114 NC::initScreen("ncmpcpp ver. " VERSION, Config.colors_enabled);
116 Actions::OriginalStatusbarVisibility = Config.statusbar_visibility;
118 if (Config.design == Design::Alternative)
119 Config.statusbar_visibility = 0;
121 Actions::setWindowsDimensions();
122 Actions::validateScreenSize();
123 Actions::initializeScreens();
125 wHeader = new NC::Window(0, 0, COLS, Actions::HeaderHeight, "", Config.header_color, NC::Border::None);
126 if (Config.header_visibility || Config.design == Design::Alternative)
127 wHeader->display();
129 wFooter = new NC::Window(0, Actions::FooterStartY, COLS, Actions::FooterHeight, "", Config.statusbar_color, NC::Border::None);
130 wFooter->setGetStringHelper(Statusbar::Helpers::getString);
132 // initialize global timer
133 Timer = boost::posix_time::microsec_clock::local_time();
135 // initialize playlist
136 myPlaylist->switchTo();
138 // local variables
139 bool key_pressed = false;
140 Key input = Key::noOp;
141 auto past = boost::posix_time::from_time_t(0);
143 /// enable mouse
144 mouseinterval(0);
145 if (Config.mouse_support)
146 mousemask(ALL_MOUSE_EVENTS, 0);
148 # ifndef WIN32
149 signal(SIGPIPE, sighandler);
150 signal(SIGWINCH, sighandler);
151 // ignore Ctrl-C
152 sigignore(SIGINT);
153 # endif // !WIN32
155 while (!Actions::ExitMainLoop)
159 if (!Mpd.Connected())
161 // reset local status info
162 Status::clear();
163 // clear mpd callback
164 wFooter->clearFDCallbacksList();
167 Mpd.Connect();
168 if (Mpd.Version() < 16)
170 Mpd.Disconnect();
171 throw MPD::ClientError(MPD_ERROR_STATE, "MPD < 0.16.0 is not supported", false);
174 catch (MPD::ClientError &e)
176 Status::handleClientError(e);
180 // update timer, status if necessary etc.
181 Status::trace(!key_pressed, true);
183 if (run_resize_screen)
185 Actions::resizeScreen(true);
186 run_resize_screen = false;
189 // header stuff
190 if ((myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLyrics)
191 && (Timer - past > boost::posix_time::milliseconds(500))
194 drawHeader();
195 past = Timer;
198 if (key_pressed)
199 myScreen->refreshWindow();
200 input = Key::read(*wFooter);
201 key_pressed = input != Key::noOp;
203 if (!key_pressed)
204 continue;
206 // The reason we want to update timer here is that if the timer is updated
207 // in Status::trace, then Key::read usually blocks for 500ms and if key is
208 // pressed 400ms after Key::read was called, we end up with Timer that is
209 // ~400ms inaccurate. On the other hand, if keys are being pressed, we don't
210 // want to update timer in both Status::trace and here. Therefore we update
211 // timer in Status::trace only if there was no recent input.
212 Timer = boost::posix_time::microsec_clock::local_time();
216 auto k = Bindings.get(input);
217 std::any_of(k.first, k.second, boost::bind(&Binding::execute, _1));
219 catch (ConversionError &e)
221 Statusbar::printf("Couldn't convert value \"%1%\" to target type", e.value());
223 catch (OutOfBounds &e)
225 Statusbar::printf("Error: %1%", e.errorMessage());
228 if (myScreen == myPlaylist)
229 myPlaylist->EnableHighlighting();
231 catch (MPD::ClientError &e)
233 Status::handleClientError(e);
235 catch (MPD::ServerError &e)
237 Status::handleServerError(e);
239 catch (std::exception &e)
241 Statusbar::printf("Unexpected error: %1%", e.what());
244 return 0;