...
 
Commits (21)
mbclient (0.8.9) stable; urgency=low
* xmas features
-- Alexis Pereda <alexis@pereda.fr> Mon, 11 Dec 2017 18:00:00 +0100
mbclient (0.8.8) stable; urgency=low
* fix AskWindow cursor position
* xmas features
-- Alexis Pereda <alexis@pereda.fr> Mon, 11 Dec 2017 14:13:54 +0100
mbclient (0.8.7) stable; urgency=low
* fix debian package permissions issue (bis)
-- Alexis Pereda <alexis@pereda.fr> Fri, 08 Dec 2017 12:49:30 +0200
-- Alexis Pereda <alexis@pereda.fr> Fri, 08 Dec 2017 12:49:30 +0100
mbclient (0.8.6) stable; urgency=low
* fix debian package permissions issue
-- Alexis Pereda <alexis@pereda.fr> Fri, 08 Dec 2017 12:44:30 +0200
-- Alexis Pereda <alexis@pereda.fr> Fri, 08 Dec 2017 12:44:30 +0100
mbclient (0.8.5) stable; urgency=low
* add copyright and changelog.gz to debian package
-- Alexis Pereda <alexis@pereda.fr> Thu, 07 Dec 2017 19:51:30 +0200
-- Alexis Pereda <alexis@pereda.fr> Thu, 07 Dec 2017 19:51:30 +0100
mbclient (0.8.4) stable; urgency=low
* fix ui glitches
-- Alexis Pereda <alexis@pereda.fr> Thu, 07 Dec 2017 00:35:34 +0200
-- Alexis Pereda <alexis@pereda.fr> Thu, 07 Dec 2017 00:35:34 +0100
mbclient (0.8.3) stable; urgency=low
......@@ -35,19 +48,19 @@ mbclient (0.8.3) stable; urgency=low
- Bug fixes:
* backspace handling in AskWindow
-- Alexis Pereda <alexis@pereda.fr> Thu, 23 Nov 2017 00:41:31 +0200
-- Alexis Pereda <alexis@pereda.fr> Thu, 23 Nov 2017 00:41:31 +0100
mbclient (0.8.2) stable; urgency=low
* fix bug with colors in some terminal emulators
-- Alexis Pereda <alexis@pereda.fr> Wed, 11 Oct 2017 22:43:44 +0200
-- Alexis Pereda <alexis@pereda.fr> Wed, 11 Oct 2017 22:43:44 +0100
mbclient (0.8.1) stable; urgency=low
* shift left/right for small jumps
-- Alexis Pereda <alexis@pereda.fr> Mon, 09 Oct 2017 13:14:39 +0200
-- Alexis Pereda <alexis@pereda.fr> Mon, 09 Oct 2017 13:14:39 +0100
mbclient (0.8.0) stable; urgency=low
......@@ -60,13 +73,13 @@ mbclient (0.8.0) stable; urgency=low
- Bug fixes:
* no segfault on connection failed
-- Alexis Pereda <alexis@pereda.fr> Fri, 06 Oct 2017 14:08:58 +0200
-- Alexis Pereda <alexis@pereda.fr> Fri, 06 Oct 2017 14:08:58 +0100
mbclient (0.7.5) stable; urgency=low
* uniform colors in popup windows
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 23:11:44 +0200
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 23:11:44 +0100
mbclient (0.7.4) stable; urgency=low
......@@ -74,13 +87,13 @@ mbclient (0.7.4) stable; urgency=low
- fix background color in Window
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 23:00:13 +0200
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 23:00:13 +0100
mbclient (0.7.4) stable; urgency=low
* buf fix
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 22:28:02 +0200
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 22:28:02 +0100
mbclient (0.7.2) stable; urgency=low
......@@ -93,7 +106,7 @@ mbclient (0.7.2) stable; urgency=low
- Changes:
* ignore empty answers in AskWindow
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 22:13:57 +0200
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 22:13:57 +0100
mbclient (0.7.1) stable; urgency=low
......@@ -110,7 +123,7 @@ mbclient (0.7.1) stable; urgency=low
* fixed never empty current music
* music list updating
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 18:49:18 +0200
-- Alexis Pereda <alexis@pereda.fr> Wed, 04 Oct 2017 18:49:18 +0100
mbclient (0.7.0) stable; urgency=low
......@@ -126,29 +139,29 @@ mbclient (0.7.0) stable; urgency=low
- Bug fixes in UI
-- Alexis Pereda <alexis@pereda.fr> Tue, 03 Oct 2017 13:46:41 +0200
-- Alexis Pereda <alexis@pereda.fr> Tue, 03 Oct 2017 13:46:41 +0100
mbclient (0.6.3) stable; urgency=low
* fix Debian jessie build
-- Alexis Pereda <alexis@pereda.fr> Sun, 01 Oct 2017 10:22:39 +0200
-- Alexis Pereda <alexis@pereda.fr> Sun, 01 Oct 2017 10:22:39 +0100
mbclient (0.6.2) stable; urgency=low
* small UI improvements
* thread for reception processing
-- Alexis Pereda <alexis@pereda.fr> Sat, 30 Sep 2017 19:17:15 +0200
-- Alexis Pereda <alexis@pereda.fr> Sat, 30 Sep 2017 19:17:15 +0100
mbclient (0.6.1) stable; urgency=low
* compatibility with jessie
-- Alexis Pereda <alexis@pereda.fr> Wed, 20 Sep 2017 21:12:55 +0200
-- Alexis Pereda <alexis@pereda.fr> Wed, 20 Sep 2017 21:12:55 +0100
mbclient (0.6.0) stable; urgency=low
* Initial Release.
-- Alexis Pereda <alexis@pereda.fr> Tue, 12 Sep 2017 18:49:26 +0200
-- Alexis Pereda <alexis@pereda.fr> Tue, 12 Sep 2017 18:49:26 +0100
......@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1)
get_filename_component(project_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(${project_name})
set(MBCLIENT_VERSION "0.8.7")
set(MBCLIENT_VERSION "0.8.9")
set(EXTENSION "cpp")
......
Package: mbclient
Version: 0.8.7
Version: 0.8.9
Architecture:
Installed-Size:
Depends: libncursesw5 (>= 5.9), libtinfo5 (>= 5.9), libc6 (>= 2.19), libstdc++6 (>= 4.9), libgcc1 (>= 1:4.9.2)
......
Package: mbclient
Version: 0.8.7
Version: 0.8.9
Architecture:
Installed-Size:
Depends: libncursesw5 (>= 6.0), libtinfo5 (>= 6.0), libc6 (>= 2.24), libstdc++6 (>= 6.0), libgcc1 (>= 1:6.0)
......
......@@ -18,12 +18,14 @@ AskWindow::AskWindow(std::size_t x, std::size_t y, std::size_t w, std::size_t h,
std::string AskWindow::show(WindowsMgr &mgr, int layer) {
std::string before, after;
{
Window win{_x, _y, _w, _h, _title};
Window win;
std::size_t cursorX{_x+1}, cursorY{_y+1};
mgr.add(win, layer);
win.setColors(_c, ColorZone::Text);
win.setTitle(_title);
win.setText("");
mgr.add(win, layer);
win.resize(_x, _y, _w, _h);
Ncurses::call(wrefresh, win);
......
......@@ -64,6 +64,9 @@ void Ncurses::initialize() {
Ncurses::call(start_color);
if(_canChangeColors) {
Ncurses::call(init_color, COLOR_BLACK, 0, 0, 0);
Ncurses::call(init_color, T28, 0, 527, 0);
Ncurses::call(init_color, T88, 527, 0, 0);
Ncurses::call(init_color, T124, 683, 0, 0);
Ncurses::call(init_color, T237, 227, 227, 227);
for(int bg = Black; bg < Count; ++bg)
......@@ -78,7 +81,7 @@ void Ncurses::initialize() {
Ncurses::call(refresh);
Ncurses::call(timeout, 10);
Ncurses::call(timeout, 50);
signal(SIGWINCH, resized);
......@@ -138,12 +141,17 @@ bool Ncurses::hasKey() {
}
void Ncurses::resized(int) {
release();
initialize();
static bool alreadyExecuting = false;
if(_resizeHandler) _resizeHandler();
if(!alreadyExecuting) {
alreadyExecuting = true;
release();
initialize();
Ncurses::call(refresh);
if(_resizeHandler) _resizeHandler();
alreadyExecuting = false;
}
}
void Ncurses::saveCursor() {
......
......@@ -22,6 +22,8 @@ enum class ColorZone { Border, Title, Text, Global, Count };
enum Color {
Black, Red, Green, Yellow, Blue, Magenta, Cyan, White,
T28,
T88, T124,
T237,
Count
......@@ -98,6 +100,15 @@ public:
return std::forward<F>(f)(std::forward<Args>(args)...);
}
/* ***** */
template<typename... Args>
static void print(WINDOW *_winptr, ColorPair const&colors, char const*fmt, Args&&... args) {
Ncurses::call(wattron, _winptr, colors);
Ncurses::call(wprintw, _winptr, fmt, std::forward<Args>(args)...);
Ncurses::call(wattroff, _winptr, colors);
}
private:
static void resized(int);
};
......
......@@ -34,10 +34,11 @@ int PopupWindow::showSimple(std::string const&title, std::vector<std::string> co
std::size_t h = content.size()+2;
{
Window win{_x, _yOffset+((_hMax-h)>>1), _w, h, title};
win.setColors(_c, ColorZone::Global);
win.setColors(_c, ColorZone::Text);
win.setColors(_c, ColorZone::Border);
win.setColors(_c, ColorZone::Title);
win.setText(content);
win.setContent(content);
mgr.add(win, layer);
key = Ncurses::waitKeyH();
......
......@@ -8,10 +8,28 @@
namespace cfy {
namespace ui {
Window::Window():
_updated{false},
_x{0}, _y{0}, _w{0}, _h{0},
_winptr{nullptr},
_redecorateOnRepaint{true},
_colorsTextStep{0},
_colorsText{
{White, Black}
},
_colors{
{White, Black},
{White, Black},
{White, Black},
{White, Black},
} {
}
Window::Window(std::size_t x, std::size_t y, std::size_t w, std::size_t h, std::string const&title):
_updated{false},
_x{x}, _y{y}, _w{w}, _h{h},
_winptr{newwin(h, w, y, x)},
_winptr{Ncurses::call(newwin, h, w, y, x)},
_redecorateOnRepaint{true},
_colorsTextStep{0},
_colorsText{
{White, Black}
......@@ -23,6 +41,8 @@ Window::Window(std::size_t x, std::size_t y, std::size_t w, std::size_t h, std::
{White, Black},
} {
Ncurses::call(wclear, _winptr);
decorate();
setTitle(title);
}
......@@ -31,50 +51,70 @@ Window::Window(Window &&o):
_x{std::move(o._x)}, _y{std::move(o._y)}, _w{std::move(o._w)}, _h{std::move(o._h)},
_winptr{std::move(o._winptr)},
_title{std::move(o._title)}, _text{std::move(o._text)},
_redecorateOnRepaint{std::move(o._redecorateOnRepaint)},
_colorsTextStep{std::move(o._colorsTextStep)},
_colorsText{std::move(o._colorsText)} {
o._winptr = nullptr;
std::swap(_colors, o._colors);
decorate();
setTitle(_title);
}
Window::~Window() {
release();
}
void Window::release() {
if(!_winptr) return;
Ncurses::call(wborder, _winptr, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
Ncurses::call(werase, _winptr);
Ncurses::call(wrefresh, _winptr);
Ncurses::call(delwin, _winptr);
_winptr = nullptr;
}
void Window::resize(std::size_t x, std::size_t y, std::size_t w, std::size_t h) {
release();
_x = x;
_y = y;
_w = w;
_h = h;
_winptr = Ncurses::call(newwin, h, w, y, x);
decorate();
{
std::lock_guard<std::mutex> lock{_mutexUpdated};
_updated = true;
}
}
void Window::clear() {
void Window::decorate() {
if(!_winptr) return;
Ncurses::call(werase, _winptr);
Ncurses::call(wbkgd, _winptr, _colors[static_cast<int>(ColorZone::Global)].bg);
Ncurses::call(wattron, _winptr, _colors[static_cast<int>(ColorZone::Border)]);
Ncurses::call(wborder, _winptr, '|', '|', '-', '-', '+', '+', '+', '+');
Ncurses::call(wattroff, _winptr, _colors[static_cast<int>(ColorZone::Border)]);
if(_title.size()) {
Ncurses::call(wmove, _winptr, 0, (_w-_title.size()-4)/2);
Ncurses::call(wattron, _winptr, _colors[static_cast<int>(ColorZone::Title)]);
Ncurses::call(wprintw, _winptr, " %s ", _title.c_str());
Ncurses::call(wattroff, _winptr, _colors[static_cast<int>(ColorZone::Title)]);
}
Ncurses::call(wrefresh, _winptr);
}
void Window::setTitle(std::string const&title) {
_title = title;
_updated = true;
{
std::lock_guard<std::mutex> lock{_mutexUpdated};
_updated = true;
}
}
void Window::setText(std::string const&text) {
_text = text;
_updated = true;
{
std::lock_guard<std::mutex> lock{_mutexUpdated};
_updated = true;
}
}
void Window::setText(Content const&tl) {
void Window::setContent(Content const&tl) {
std::string text;
std::size_t textWidth = _w-2;
......@@ -107,55 +147,47 @@ void Window::setText(Content const&tl) {
}
void Window::repaint() {
if(!_winptr) return;
std::lock_guard<std::mutex> lock{_mutexUpdated};
Content content;
std::istringstream iss{_text};
for(std::string line; std::getline(iss, line);) content.push_back(line);
clear();
if(_redecorateOnRepaint) decorate();
if(_title.size()) {
Ncurses::call(wmove, _winptr, 0, (_w-_title.size()-4)/2);
Ncurses::call(wattron, _winptr, _colors[static_cast<int>(ColorZone::Title)]);
Ncurses::call(wprintw, _winptr, " %s ", _title.c_str());
Ncurses::call(wattroff, _winptr, _colors[static_cast<int>(ColorZone::Title)]);
}
for(std::size_t i = 0; i < content.size() && i < (_h-2); ++i) {
std::string const&line = content[i];
Ncurses::call(wmove, _winptr, 1+i, 1);
if(_colorsText.empty()) {
for(char const&c: line) {
if(_colorChars.count(c)) {
Ncurses::call(wattron, _winptr, _colorChars.at(c));
Ncurses::call(wprintw, _winptr, "%c", c);
Ncurses::call(wattroff, _winptr, _colorChars.at(c));
} else
Ncurses::call(wprintw, _winptr, "%c", c);
}
} else if(_colorsTextStep) {
if(_colorsText.size() && _colorsTextStep) {
ColorPairs::const_iterator cIt = std::begin(_colorsText);
for(std::size_t j = 0; j < line.size(); ++j) {
char const&c = line[j];
if(_colorChars.count(c)) {
Ncurses::call(wattron, _winptr, _colorChars.at(c));
Ncurses::call(wprintw, _winptr, "%c", c);
Ncurses::call(wattroff, _winptr, _colorChars.at(c));
} else {
Ncurses::call(wattron, _winptr, *cIt);
Ncurses::call(wprintw, _winptr, "%c", c);
Ncurses::call(wattroff, _winptr, *cIt);
}
if(_colorChars.count(c)) Ncurses::print(_winptr, _colorChars.at(c), "%c", c);
else Ncurses::print(_winptr, *cIt, "%c", c);
if(!((j+1)%_colorsTextStep)) {
++cIt;
if(cIt == std::end(_colorsText)) cIt = std::begin(_colorsText);
}
}
} else {
} else if(_colorChars.size()) {
for(char const&c: line) {
if(_colorChars.count(c)) {
Ncurses::call(wattron, _winptr, _colorChars.at(c));
Ncurses::call(wprintw, _winptr, "%c", c);
Ncurses::call(wattroff, _winptr, _colorChars.at(c));
} else
Ncurses::call(wprintw, _winptr, "%c", c);
if(_colorChars.count(c)) Ncurses::print(_winptr, _colorChars.at(c), "%c", c);
else Ncurses::print(_winptr, _colors[static_cast<int>(ColorZone::Text)], "%c", c);
}
}
} else Ncurses::print(_winptr, _colors[static_cast<int>(ColorZone::Text)], "%s", line.c_str());
}
Ncurses::call(wrefresh, _winptr);
......@@ -165,12 +197,14 @@ void Window::repaint() {
void Window::setColor(Color c, ColorZone z, ColorDepth d) {
if(d == ColorDepth::Foreground) _colors[static_cast<int>(z)].fg = c;
else _colors[static_cast<int>(z)].bg = c;
Ncurses::call(wclear, _winptr);
if(_winptr) Ncurses::call(wclear, _winptr);
}
void Window::setColors(ColorPair c, ColorZone z) {
_colors[static_cast<int>(z)] = c;
Ncurses::call(wclear, _winptr);
if(_winptr) Ncurses::call(wclear, _winptr);
}
}
......
......@@ -18,39 +18,51 @@ public:
using ColorChars = std::map<char, ColorPair>;
private:
std::mutex _mutexUpdated;
bool _updated;
std::size_t _x, _y, _w, _h;
WINDOW *_winptr;
std::string _title, _text;
bool _redecorateOnRepaint;
std::size_t _colorsTextStep;
ColorPairs _colorsText;
ColorPair _colors[static_cast<int>(ColorZone::Count)];
ColorChars _colorChars;
public:
Window() = delete;
Window();
Window(std::size_t x, std::size_t y, std::size_t w, std::size_t h, std::string const&title = "");
Window(Window const&) = delete;
Window(Window &&o);
~Window();
void release();
operator WINDOW*() const { return _winptr; }
bool updated() const { return _updated; }
bool updated() {
std::lock_guard<std::mutex> lock{_mutexUpdated};
return _updated;
}
void resize(std::size_t x, std::size_t y, std::size_t w, std::size_t h);
std::size_t x() const { return _x; }
std::size_t y() const { return _y; }
std::size_t w() const { return _w; }
std::size_t h() const { return _h; }
void clear();
void setRedecorateOnRepaint(bool b) { _redecorateOnRepaint = b; }
void decorate();
void setTitle(std::string const&title);
void setText(std::string const&text);
void setText(std::string const&text, ColorPair c) { setTextColors(0, {c}); setText(text); }
void setText(Content const&tl);
void setText(Content const&tl, ColorPair c) { setTextColors(0, {c}); setText(tl); }
void setTextC(std::string const&text, ColorPair c) { setColors(c, ColorZone::Text); setText(text); }
void setContent(Content const&tl);
void setContentC(Content const&tl, ColorPair c) { setColors(c, ColorZone::Text); setContent(tl); }
void repaint();
......
......@@ -11,19 +11,23 @@ WindowsMgr::WindowsMgr():
}
void WindowsMgr::add(Window &window, int layer) {
std::lock_guard<std::mutex> lock{_mutex};
_layers[layer].push_back(&window);
}
void WindowsMgr::remove(Window &window) {
std::lock_guard<std::mutex> lock{_mutex};
for(Windows &windows: _layers)
windows.erase(std::remove(std::begin(windows), std::end(windows), &window), std::end(windows));
}
void WindowsMgr::removeAll() {
std::lock_guard<std::mutex> lock{_mutex};
for(Windows &windows: _layers) windows.clear();
}
void WindowsMgr::repaint(bool force) {
std::lock_guard<std::mutex> lock{_mutex};
bool previousLayerUpdated = force;
for(Windows &windows: _layers) {
bool currentLayerUpdated = false;
......
......@@ -18,6 +18,8 @@ public:
using Layers = std::array<Windows, LayerCount>;
private:
std::mutex _mutex;
Layers _layers;
public:
......
#include <csignal>
#include <iostream>
#include "cli.h"
......@@ -5,11 +6,21 @@
#include "mbclient_version.h"
int main(int argc, char **argv) {
struct sigaction actionQuit;
Arguments args = processCLI(argc, argv);
if(args.version) {
std::cout << "mbclient " << MBCLIENT_VERSION << std::endl;
return 0;
}
actionQuit.sa_handler = [](int) { MbClient::terminated = true; };
sigemptyset(&actionQuit.sa_mask);
actionQuit.sa_flags = 0;
sigaction(SIGINT, &actionQuit, NULL);
sigaction(SIGTERM, &actionQuit, NULL);
cfy::ui::Ncurses::setColorsWanted(args.colorsWanted);
cfy::ui::Ncurses::initialize();
return MbClient{args}.run();
......
This diff is collapsed.
......@@ -5,6 +5,7 @@
#include <deque>
#include <mutex>
#include <net/client.h>
#include <thread>
#include "cli.h"
#include "event.h"
......@@ -13,6 +14,13 @@
#include "ui.h"
class MbClient {
public:
using ProcessMusicListFunction = std::function<void(Ui::Content&)>;
public:
static bool volatile terminated;
private:
Arguments _args;
cfy::net::Client _client;
......@@ -26,6 +34,9 @@ class MbClient {
std::string _currentMusic;
std::deque<std::string> _musicList;
std::mutex _mutexProcessMLs;
std::deque<ProcessMusicListFunction const*> _processMLs;
bool _subscribed;
int _currentVolume;
bool _keepNumber;
......@@ -35,15 +46,24 @@ class MbClient {
float _currentProgress;
std::mutex _mutexReceived, _mutexUiResized, _mutexMusicListUpdated;
std::condition_variable _cvReceived, _cvUiResized, _cvMusicListUpdated;
std::mutex _mutexReceived, _mutexMusicListUpdated;
std::condition_variable _cvReceived, _cvMusicListUpdated;
bool volatile _connected;
bool volatile _uiResized;
std::thread _threadXmas;
public:
MbClient(Arguments const&args);
std::string const&currentPlaylist() const { return _plCur; }
Ui &ui() { return _ui; }
void addProcessMusicListFunction(ProcessMusicListFunction const*f);
void eraseProcessMusicListFunction(ProcessMusicListFunction const*f);
void signalMusicListUpdated();
int run();
bool connect();
......@@ -51,8 +71,10 @@ public:
void process(std::string const&data);
void resizeUI();
void updateMusicList();
private:
void xmas();
};
#endif
......@@ -10,6 +10,7 @@ Ui::Ui():
_w{Ncurses::width()},
_h{Ncurses::height()} {
initializeWindows();
resizeWindows();
Ncurses::onResizeEvent(std::bind(&Ui::resizeEvent, this));
}
......@@ -18,8 +19,8 @@ Ui::~Ui() {
Ncurses::release();
}
void Ui::clear() {
for(auto &pair: _windows) std::get<1>(pair).clear();
void Ui::decorate() {
for(auto &pair: _windows) std::get<1>(pair).decorate();
}
void Ui::repaint(bool force) {
......@@ -85,37 +86,48 @@ void Ui::processPopupQueue() {
void Ui::initializeWindows() {
_windowsMgr.removeAll();
_windows.clear();
_windows.emplace(WindowId::List, Window{0, 0, _w, _h-6});
_windows.emplace(WindowId::Progress, Window{0, _h-3, _w, 3 });
_windows.emplace(WindowId::Subscribed, Window{0, _h-6, 3, 3 });
_windows.emplace(WindowId::PlCur, Window{3, _h-6, _w-16, 3 });
_windows.emplace(WindowId::State, Window{_w-13, _h-6, 7, 3 });
_windows.emplace(WindowId::Volume, Window{_w-6, _h-6, 6, 3 });
_windowsMgr.add(_windows.at(WindowId::List));
_windowsMgr.add(_windows.at(WindowId::Progress));
_windowsMgr.add(_windows.at(WindowId::Subscribed));
_windowsMgr.add(_windows.at(WindowId::PlCur));
_windowsMgr.add(_windows.at(WindowId::State));
_windowsMgr.add(_windows.at(WindowId::Volume));
window(WindowId::List).setTitle("Music list");
window(WindowId::PlCur).setTitle("Current playlist");
window(WindowId::Progress).setColors({White, White}, ColorZone::Text);
window(WindowId::Progress).setTextColors(0, {{White, White}});
window(WindowId::Subscribed).setColors({Green, Green}, ColorZone::Text);
window(WindowId::Subscribed).setTextColors(0, {{Green, Green}});
{
std::lock_guard<std::mutex> lock{_mutexWindows};
_windows.clear();
_windows.emplace(WindowId::List, Window{});
_windows.emplace(WindowId::Progress, Window{});
_windows.emplace(WindowId::Subscribed, Window{});
_windows.emplace(WindowId::PlCur, Window{});
_windows.emplace(WindowId::State, Window{});
_windows.emplace(WindowId::Volume, Window{});
_windowsMgr.add(_windows.at(WindowId::List));
_windowsMgr.add(_windows.at(WindowId::PlCur));
_windowsMgr.add(_windows.at(WindowId::Progress));
_windowsMgr.add(_windows.at(WindowId::Subscribed));
_windowsMgr.add(_windows.at(WindowId::State));
_windowsMgr.add(_windows.at(WindowId::Volume));
}
windowDo(WindowId::List, &Window::setTitle, "Music list");
windowDo(WindowId::PlCur, &Window::setTitle, "Current playlist");
windowDo(WindowId::Progress, &Window::setTextColors, 1, ColorPairs{{White, White}});
windowDo(WindowId::Subscribed, &Window::setColorChar, 'S', ColorPair{Green, Green});
}
void Ui::resizeWindows() {
std::lock_guard<std::mutex> lock{_mutexWindows};
_windows.at(WindowId::List).resize( 0, 0, _w, _h-6);
_windows.at(WindowId::Progress).resize( 0, _h-3, _w, 3 );
_windows.at(WindowId::Subscribed).resize( 0, _h-6, 3, 3 );
_windows.at(WindowId::PlCur).resize( 3, _h-6, _w-16, 3 );
_windows.at(WindowId::State).resize( _w-13, _h-6, 7, 3 );
_windows.at(WindowId::Volume).resize( _w-6, _h-6, 6, 3 );
}
void Ui::resizeEvent() {
_w = Ncurses::width();
_h = Ncurses::height();
initializeWindows();
resizeWindows();
if(_resizeHandler) _resizeHandler();
repaint(true);
}
......@@ -20,6 +20,7 @@ public:
using Color = cfy::ui::Color;
using ColorPair = cfy::ui::ColorPair;
using ColorPairs = cfy::ui::Window::ColorPairs;
using ResizeHandler = cfy::ui::Ncurses::ResizeHandler;
......@@ -36,6 +37,8 @@ public:
private:
std::size_t _w, _h;
cfy::ui::WindowsMgr _windowsMgr;
std::mutex _mutexWindows;
Windows _windows;
std::mutex _mutexPopupQueue, _mutexAskQueue;
......@@ -48,10 +51,14 @@ public:
Ui();
~Ui();
void clear();
void decorate();
void repaint(bool force = false);
Window &window(WindowId const&wid) { return _windows.at(wid); }
template<typename F, typename... Args>
decltype(auto) windowDo(WindowId const&wid, F &&f, Args&&... args) {
std::lock_guard<std::mutex> lock{_mutexWindows};
return (_windows.at(wid).*std::forward<F>(f))(std::forward<Args>(args)...);
}
std::string askFor(std::string const&title);
int popup(std::string const&title, std::string const&content, ColorPair c = {Color::White, Color::T237});
......@@ -68,6 +75,7 @@ public:
private:
void initializeWindows();
void resizeWindows();
void resizeEvent();
};
......
#include <algorithm>
#include <random>
#include "mbclient.h"
#include "xmas.h"
void Xmas::run() {
using namespace cfy::ui;
MbClient::ProcessMusicListFunction const lProcessML = [&](Ui::Content &tl) { processML(tl); };
_mbClient->ui().windowDo(Ui::WindowId::Progress, &Window::setTextColors, 2, Window::ColorPairs{{Green, Green}, {T124, T124}});
_mbClient->ui().windowDo(Ui::WindowId::List, &Window::setColorChar, '~', ColorPair{White, White});
_mbClient->addProcessMusicListFunction(&lProcessML);
while(_mbClient->currentPlaylist() == "xmas") {
std::size_t w{_mbClient->ui().windowDo(Ui::WindowId::List, &Window::w)}, h{_mbClient->ui().windowDo(Ui::WindowId::List, &Window::h)};
{
std::lock_guard<std::mutex> lock{_mutexParticles};
for(Particle &p: _particles) {
p.row += 1+_genYOffset(_rng);
p.column += _genXOffset(_rng);
}
_particles.erase(
std::remove_if(std::begin(_particles), std::end(_particles),
[hMax=static_cast<int>(h)-2](Particle const&p) {
return p.row > hMax;
}
),
std::end(_particles)
);
std::uniform_int_distribution<int> uniformDist{0, static_cast<int>(w)-2};
int n = _genNParticles(_rng);
for(int i = 0; i < n; ++i) {
_particles.emplace_back(0, uniformDist(_rng));
}
}
_mbClient->signalMusicListUpdated();
std::this_thread::sleep_for(std::chrono::milliseconds{200});
}
_mbClient->eraseProcessMusicListFunction(&lProcessML);
_mbClient->ui().windowDo(Ui::WindowId::Progress, &Window::setTextColors, 1, Window::ColorPairs{{White, White}});
_mbClient->ui().windowDo(Ui::WindowId::List, &Window::unsetColorChar, '~');
_mbClient->signalMusicListUpdated();
}
void Xmas::processML(Ui::Content &tl) {
int w{static_cast<int>(_mbClient->ui().windowDo(Ui::WindowId::List, &cfy::ui::Window::w))-2},
h{static_cast<int>(_mbClient->ui().windowDo(Ui::WindowId::List, &cfy::ui::Window::h))-2};
std::lock_guard<std::mutex> lock{_mutexParticles};
while(static_cast<int>(tl.size()) != h) tl.emplace_back();
for(std::string &line: tl)
while(w > static_cast<int>(line.size())) line.push_back(' ');
for(Particle const&p: _particles)
if(p.row >= 0 && p.row < h && p.column >= 0 && p.column < w)
tl[p.row][p.column] = '~';
}
#ifndef XMAS_H
#define XMAS_H
#include <mutex>
#include <random>
#include <vector>
#include "ui.h"
class MbClient;
class Xmas {
public:
struct Particle {
int row, column;
Particle() = delete;
Particle(int row, int column): row{row}, column{column} {}
};
private:
std::random_device _randomDevice;
std::mt19937 _rng;
std::uniform_int_distribution<int> _genXOffset;
std::bernoulli_distribution _genYOffset;
std::uniform_int_distribution<int> _genNParticles;
MbClient *_mbClient;
std::vector<Particle> _particles;
std::mutex _mutexParticles;
public:
Xmas(MbClient *mbClient):
_randomDevice{},
_rng{_randomDevice()},
_genXOffset{-1, 1},
_genYOffset{.2},
_genNParticles{0, 1},
_mbClient{mbClient} {
}
void run();
private:
void processML(Ui::Content &tl);
};
#endif