strbuffer: change basic_buffer to BasicBuffer
[ncmpcpp.git] / src / ncmpcpp.cpp
blobf4578de4ce91ee30152831b3a7266f93ab98442e
1 /***************************************************************************
2 * Copyright (C) 2008-2012 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>
25 #include <sys/time.h>
27 #include <iostream>
28 #include <fstream>
29 #include <stdexcept>
31 #include "mpdpp.h"
33 #include "actions.h"
34 #include "bindings.h"
35 #include "browser.h"
36 #include "cmdargs.h"
37 #include "global.h"
38 #include "helpers.h"
39 #include "lyrics.h"
40 #include "playlist.h"
41 #include "settings.h"
42 #include "status.h"
43 #include "statusbar.h"
44 #include "visualizer.h"
45 #include "title.h"
47 namespace
49 std::ofstream errorlog;
50 std::streambuf *cerr_buffer;
52 # if !defined(WIN32)
53 void sighandler(int signal)
55 if (signal == SIGPIPE)
57 Statusbar::msg("SIGPIPE (broken pipe signal) received");
59 else if (signal == SIGWINCH)
61 Action::ResizeScreen(true);
64 # endif // !WIN32
66 void do_at_exit()
68 // restore old cerr buffer
69 std::cerr.rdbuf(cerr_buffer);
70 errorlog.close();
71 Mpd.Disconnect();
72 # ifndef USE_PDCURSES // destroying screen somehow crashes pdcurses
73 NC::destroyScreen();
74 # endif // USE_PDCURSES
75 windowTitle("");
79 int main(int argc, char **argv)
81 using Global::myScreen;
82 using Global::myOldScreen;
83 using Global::myPrevScreen;
84 using Global::myLockedScreen;
85 using Global::myInactiveScreen;
87 using Global::wHeader;
88 using Global::wFooter;
90 using Global::ShowMessages;
91 using Global::VolumeState;
92 using Global::Timer;
94 srand(time(0));
95 setlocale(LC_ALL, "");
96 std::locale::global(std::locale(""));
98 Config.CheckForCommandLineConfigFilePath(argv, argc);
100 Config.SetDefaults();
101 Config.Read();
102 Config.GenerateColumns();
104 if (!Bindings.read(Config.ncmpcpp_directory + "bindings"))
105 return 1;
106 Bindings.generateDefaults();
108 if (getenv("MPD_HOST"))
109 Mpd.SetHostname(getenv("MPD_HOST"));
110 if (getenv("MPD_PORT"))
111 Mpd.SetPort(atoi(getenv("MPD_PORT")));
113 if (Config.mpd_host != "localhost")
114 Mpd.SetHostname(Config.mpd_host);
115 if (Config.mpd_port != 6600)
116 Mpd.SetPort(Config.mpd_port);
118 Mpd.SetTimeout(Config.mpd_connection_timeout);
119 Mpd.SetIdleEnabled(Config.enable_idle_notifications);
121 if (argc > 1)
122 ParseArgv(argc, argv);
124 if (!Action::ConnectToMPD())
125 exit(1);
127 if (Mpd.Version() < 16)
129 std::cout << "MPD < 0.16.0 is not supported, please upgrade.\n";
130 exit(1);
133 CreateDir(Config.ncmpcpp_directory);
135 // always execute these commands, even if ncmpcpp use exit function
136 atexit(do_at_exit);
138 // redirect std::cerr output to ~/.ncmpcpp/error.log file
139 errorlog.open((Config.ncmpcpp_directory + "error.log").c_str(), std::ios::app);
140 cerr_buffer = std::cerr.rdbuf();
141 std::cerr.rdbuf(errorlog.rdbuf());
143 NC::initScreen("ncmpcpp ver. " VERSION, Config.colors_enabled);
145 Action::OriginalStatusbarVisibility = Config.statusbar_visibility;
147 if (!Config.titles_visibility)
148 wattron(stdscr, COLOR_PAIR(Config.main_color));
150 if (Config.new_design)
151 Config.statusbar_visibility = 0;
153 Action::SetWindowsDimensions();
154 Action::ValidateScreenSize();
156 wHeader = new NC::Window(0, 0, COLS, Action::HeaderHeight, "", Config.header_color, NC::brNone);
157 if (Config.header_visibility || Config.new_design)
158 wHeader->display();
160 wFooter = new NC::Window(0, Action::FooterStartY, COLS, Action::FooterHeight, "", Config.statusbar_color, NC::brNone);
161 wFooter->setTimeout(500);
162 wFooter->setGetStringHelper(Statusbar::Helpers::getString);
163 if (Mpd.SupportsIdle())
164 wFooter->addFDCallback(Mpd.GetFD(), Statusbar::Helpers::mpd);
165 wFooter->createHistory();
167 // initialize screens to browser as default previous screen
168 myScreen = myBrowser;
169 myPrevScreen = myBrowser;
170 myOldScreen = myBrowser;
172 // initialize global timer
173 gettimeofday(&Timer, 0);
175 // go to playlist
176 myPlaylist->SwitchTo();
177 myPlaylist->UpdateTimer();
179 // go to startup screen
180 if (Config.startup_screen != myScreen)
181 Config.startup_screen->SwitchTo();
183 Mpd.SetStatusUpdater(Status::update, 0);
184 Mpd.SetErrorHandler(Status::handleError, 0);
186 // local variables
187 Key input(0, Key::Standard);
188 timeval past = { 0, 0 };
189 // local variables end
191 # ifndef WIN32
192 signal(SIGPIPE, sighandler);
193 signal(SIGWINCH, sighandler);
194 # endif // !WIN32
196 mouseinterval(0);
197 if (Config.mouse_support)
198 mousemask(ALL_MOUSE_EVENTS, 0);
200 Mpd.OrderDataFetching();
201 if (Config.jump_to_now_playing_song_at_start)
203 Status::trace();
204 int curr_pos = Mpd.GetCurrentSongPos();
205 if (curr_pos >= 0)
206 myPlaylist->Items->highlight(curr_pos);
209 while (!Action::ExitMainLoop)
211 if (!Mpd.Connected())
213 if (!wFooter->FDCallbacksListEmpty())
214 wFooter->clearFDCallbacksList();
215 Statusbar::msg("Attempting to reconnect...");
216 if (Mpd.Connect())
218 Statusbar::msg("Connected to %s", Mpd.GetHostname().c_str());
219 if (Mpd.SupportsIdle())
221 wFooter->addFDCallback(Mpd.GetFD(), Statusbar::Helpers::mpd);
222 Mpd.OrderDataFetching(); // we need info about new connection
224 ShowMessages = false;
225 # ifdef ENABLE_VISUALIZER
226 myVisualizer->ResetFD();
227 if (myScreen == myVisualizer)
228 myVisualizer->SetFD();
229 myVisualizer->FindOutputID();
230 # endif // ENABLE_VISUALIZER
234 Status::trace();
236 ShowMessages = true;
238 // header stuff
239 if (((Timer.tv_sec == past.tv_sec && Timer.tv_usec >= past.tv_usec+500000) || Timer.tv_sec > past.tv_sec)
240 && (myScreen == myPlaylist || myScreen == myBrowser || myScreen == myLyrics)
243 drawHeader();
244 past = Timer;
247 // header stuff end
249 if (input != Key::noOp)
250 myScreen->RefreshWindow();
251 input = Key::read(*wFooter);
253 if (input == Key::noOp)
254 continue;
256 auto k = Bindings.get(input);
257 for (; k.first != k.second; ++k.first)
259 Binding &b = k.first->second;
260 if (b.isSingle())
262 if (b.action()->Execute())
263 break;
265 else
267 auto chain = b.chain();
268 for (auto it = chain->begin(); it != chain->end(); ++it)
269 if (!(*it)->Execute())
270 break;
271 break;
275 if (myScreen == myPlaylist)
276 myPlaylist->EnableHighlighting();
278 # ifdef ENABLE_VISUALIZER
279 // visualizer sets timeout to 40ms, but since only it needs such small
280 // value, we should restore defalt one after switching to another screen.
281 if (wFooter->getTimeout() < 500
282 && !(myScreen == myVisualizer || myLockedScreen == myVisualizer || myInactiveScreen == myVisualizer)
284 wFooter->setTimeout(500);
285 # endif // ENABLE_VISUALIZER
287 return 0;