1 /***************************************************************************
2 * Copyright (C) 2008-2016 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>
36 #include "screens/browser.h"
38 #include "configuration.h"
41 #include "screens/lyrics.h"
42 #include "screens/outputs.h"
43 #include "screens/playlist.h"
46 #include "statusbar.h"
47 #include "screens/visualizer.h"
49 #include "utility/conversion.h"
51 namespace ph
= std::placeholders
;
55 std::ofstream errorlog
;
56 std::streambuf
*cerr_buffer
;
58 volatile bool run_resize_screen
= false;
60 void sighandler(int sig
)
63 run_resize_screen
= true;
64 #if defined(__sun) && defined(__SVR4)
65 // in solaris it is needed to reinstall the handler each time it's executed
66 signal(sig
, sighandler
);
67 #endif // __sun && __SVR4
72 // restore old cerr buffer
73 std::cerr
.rdbuf(cerr_buffer
);
82 int main(int argc
, char **argv
)
84 using Global::myScreen
;
86 using Global::wHeader
;
87 using Global::wFooter
;
89 using Global::VolumeState
;
92 std::setlocale(LC_ALL
, "");
93 std::locale::global(Charset::internalLocale());
95 if (!configure(argc
, argv
))
98 // always execute these commands, even if ncmpcpp use exit function
101 // redirect std::cerr output to ~/.ncmpcpp/error.log file
102 errorlog
.open((Config
.ncmpcpp_directory
+ "error.log").c_str(), std::ios::app
);
103 cerr_buffer
= std::cerr
.rdbuf();
104 std::cerr
.rdbuf(errorlog
.rdbuf());
107 signal(SIGWINCH
, sighandler
);
109 NC::initScreen(Config
.colors_enabled
, Config
.mouse_support
);
111 Actions::OriginalStatusbarVisibility
= Config
.statusbar_visibility
;
113 if (Config
.design
== Design::Alternative
)
114 Config
.statusbar_visibility
= 0;
116 Actions::setWindowsDimensions();
117 Actions::validateScreenSize();
118 Actions::initializeScreens();
120 wHeader
= new NC::Window(0, 0, COLS
, Actions::HeaderHeight
, "", Config
.header_color
, NC::Border());
121 if (Config
.header_visibility
|| Config
.design
== Design::Alternative
)
124 wFooter
= new NC::Window(0, Actions::FooterStartY
, COLS
, Actions::FooterHeight
, "", Config
.statusbar_color
, NC::Border());
125 wFooter
->setPromptHook(Statusbar::Helpers::mainHook
);
127 // initialize global timer
128 Timer
= boost::posix_time::microsec_clock::local_time();
130 // initialize global random number generator
131 Global::RNG
.seed(std::random_device()());
133 // initialize playlist
134 myPlaylist
->switchTo();
136 // go to startup screen
137 if (Config
.startup_screen_type
!= myScreen
->type())
139 auto startup_screen
= toScreen(Config
.startup_screen_type
);
140 assert(startup_screen
!= nullptr);
141 startup_screen
->switchTo();
144 // lock current screen and go to the slave one if applicable
145 if (Config
.startup_slave_screen_type
)
147 auto slave_screen_type
= *Config
.startup_slave_screen_type
;
148 bool screen_locked
= myScreen
->lock();
149 if (screen_locked
&& slave_screen_type
!= myScreen
->type())
151 auto slave_screen
= toScreen(slave_screen_type
);
152 assert(slave_screen
!= nullptr);
153 slave_screen
->switchTo();
154 if (!Config
.startup_slave_screen_focus
)
155 Actions::get(Actions::Type::MasterScreen
).execute();
160 bool key_pressed
= false;
161 auto input
= NC::Key::None
;
162 auto connect_attempt
= boost::posix_time::from_time_t(0);
163 auto update_environment
= static_cast<Actions::UpdateEnvironment
&>(
164 Actions::get(Actions::Type::UpdateEnvironment
));
166 while (!Actions::ExitMainLoop
)
170 if (!Mpd
.Connected() && Timer
- connect_attempt
> boost::posix_time::seconds(1))
172 connect_attempt
= Timer
;
173 // reset local status info
175 // clear mpd callback
176 wFooter
->clearFDCallbacksList();
180 if (Mpd
.Version() < 16)
183 throw MPD::ClientError(MPD_ERROR_STATE
, "MPD < 0.16.0 is not supported", false);
186 catch (MPD::ClientError
&e
)
188 Status::handleClientError(e
);
192 if (run_resize_screen
)
194 Actions::resizeScreen(true);
195 run_resize_screen
= false;
198 update_environment
.run(!key_pressed
, key_pressed
, false);
200 input
= readKey(*wFooter
);
201 key_pressed
= input
!= NC::Key::None
;
205 // The reason we want to update timer here is that if the timer is updated
206 // in Status::trace, then Key::read usually blocks for 500ms and if key is
207 // pressed 400ms after Key::read was called, we end up with Timer that is
208 // ~400ms inaccurate. On the other hand, if keys are being pressed, we don't
209 // want to update timer in both Status::trace and here. Therefore we update
210 // timer in Status::trace only if there was no recent input.
211 Timer
= boost::posix_time::microsec_clock::local_time();
215 auto k
= Bindings
.get(input
);
216 std::any_of(k
.first
, k
.second
, std::bind(&Binding::execute
, ph::_1
));
218 catch (ConversionError
&e
)
220 Statusbar::printf("Invalid value: %1%", e
.value());
222 catch (OutOfBounds
&e
)
224 Statusbar::printf("Error: %1%", e
.errorMessage());
226 catch (NC::PromptAborted
&)
228 Statusbar::printf("Action aborted");
231 if (myScreen
== myPlaylist
)
232 myPlaylist
->enableHighlighting();
234 catch (MPD::ClientError
&e
)
236 Status::handleClientError(e
);
238 catch (MPD::ServerError
&e
)
240 Status::handleServerError(e
);
242 catch (std::exception
&e
)
244 Statusbar::printf("Unexpected error: %1%", e
.what());