1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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. *
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. *
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 ***************************************************************************/
26 #include <boost/date_time/posix_time/posix_time.hpp>
27 #include <boost/locale.hpp>
38 #include "configuration.h"
47 #include "statusbar.h"
48 #include "visualizer.h"
50 #include "utility/conversion.h"
54 std::ofstream errorlog
;
55 std::streambuf
*cerr_buffer
;
56 bool run_resize_screen
= false;
59 void sighandler(int sig
)
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
78 // restore old cerr buffer
79 std::cerr
.rdbuf(cerr_buffer
);
82 # ifndef USE_PDCURSES // destroying screen somehow crashes pdcurses
84 # endif // USE_PDCURSES
89 int main(int argc
, char **argv
)
91 using Global::myScreen
;
93 using Global::wHeader
;
94 using Global::wFooter
;
96 using Global::VolumeState
;
100 std::setlocale(LC_ALL
, "");
101 std::locale::global(Charset::internalLocale());
103 if (!configure(argc
, argv
))
106 // always execute these commands, even if ncmpcpp use exit function
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
)
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();
139 bool key_pressed
= false;
140 Key input
= Key::noOp
;
141 auto past
= boost::posix_time::from_time_t(0);
145 if (Config
.mouse_support
)
146 mousemask(ALL_MOUSE_EVENTS
, 0);
149 signal(SIGPIPE
, sighandler
);
150 signal(SIGWINCH
, sighandler
);
155 while (!Actions::ExitMainLoop
)
159 if (!Mpd
.Connected())
161 // reset local status info
163 // clear mpd callback
164 wFooter
->clearFDCallbacksList();
168 if (Mpd
.Version() < 16)
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;
190 if ((myScreen
== myPlaylist
|| myScreen
== myBrowser
|| myScreen
== myLyrics
)
191 && (Timer
- past
> boost::posix_time::milliseconds(500))
199 myScreen
->refreshWindow();
200 input
= Key::read(*wFooter
);
201 key_pressed
= input
!= Key::noOp
;
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());