#include "peaclock/tui.hh" #include "ob/num.hh" #include "ob/algorithm.hh" #include "ob/string.hh" #include "ob/text.hh" #include "ob/term.hh" namespace aec = OB::Term::ANSI_Escape_Codes; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; using std::string_literals::operator""s; // bool to string static constexpr char const* btos(bool const x) { return x ? "on" : "off"; } Tui::Tui(Parg const& parg) : _pg {parg}, _colorterm {OB::Term::is_colorterm()} { _ctx.prompt.timeout = _ctx.prompt.rate.get() / _ctx.refresh_rate.get(); } bool Tui::press_to_continue(std::string const& str, char32_t val) { std::cerr << "Press " << str << " to continue"; _term_mode.set_min(1); _term_mode.set_raw(); bool res {false}; char32_t key {0}; if ((key = OB::Term::get_key()) > 0) { res = (val == 0 ? true : val == key); } _term_mode.set_cooked(); std::cerr << aec::nl; return res; } void Tui::base_config(fs::path const& path) { _ctx.base_config = path; } void Tui::load_config(fs::path const& path) { // ignore config if path equals "NONE" if (path == "NONE") { return; } // buffer for error output std::ostringstream err; if (! path.empty() && fs::exists(path)) { std::ifstream file {path}; if (file.is_open()) { std::string line; std::size_t lnum {0}; while (std::getline(file, line)) { // increase line number ++lnum; // trim leading and trailing whitespace line = OB::String::trim(line); // ignore empty line or comment if (line.empty() || OB::String::assert_rx(line, std::regex("^#[^\\r]*$"))) { continue; } if (auto const res = command(line)) { if (! res.value().first) { // source:line: level: info err << path.string() << ":" << lnum << ": " << res.value().second << "\n"; } } } } else { err << "error: could not open config file '" << path.string() << "'\n"; } } else { err << "error: the file '" << path.string() << "' does not exist\n"; } if (! err.str().empty()) { std::cerr << err.str(); if (! press_to_continue("ENTER", '\n')) { throw std::runtime_error("aborted by user"); } } } void Tui::load_hist_command(fs::path const& path) { _readline.hist_load(path); } bool Tui::mkconfig(std::string path, bool overwrite) { if (path.front() == '~') { path.replace(0, 1, std::getenv("HOME")); } fs::path fpath {path}; if (fs::exists(fpath)) { if (! fs::is_regular_file(fpath)) { set_status(false, "error: '" + fpath.lexically_normal().string() + "' is not a regular file"); return false; } if (! overwrite) { set_status(false, "error: file '" + fpath.lexically_normal().string() + "' already exists, use command 'mkconfig!' to overwrite"); return false; } } std::ofstream file {fpath}; if (! file.is_open()) { set_status(false, "error: could not open file '" + fpath.lexically_normal().string() + "'"); return false; } // timestamp std::time_t t = std::time(0); std::tm tm = *std::localtime(&t); // dump current state to file file << "# peaclock config\n" << "# file: " << fpath.lexically_normal().string() << "\n" << "# date: " << std::put_time(&tm, "%FT%TZ") << "\n\n" << "mode " << Peaclock::Mode::str(_peaclock.cfg.mode) << "\n" << "view " << Peaclock::View::str(_peaclock.cfg.view) << "\n" << "toggle " << Peaclock::Toggle::str(_peaclock.cfg.toggle) << "\n" << "stopwatch start\n" << "timer " << OB::Timer::sec_to_str(_peaclock.cfg.timer_seconds) << "\n" << "timer-exec '" << OB::String::escape(_peaclock.cfg.timer_exec) << "'\n" << "date '" << OB::String::escape(_peaclock.cfg.datefmt) << "'\n" << "locale '" << _peaclock.cfg.locale << "'\n" << "timezone '" << _peaclock.cfg.timezone << "'\n" << "fill-colon '" << OB::String::escape(_peaclock.cfg.fill_colon) << "'\n" << "fill-active '" << OB::String::escape(_peaclock.cfg.fill_active) << "'\n" << "fill-inactive '" << OB::String::escape(_peaclock.cfg.fill_inactive) << "'\n" << "rate-input " << _ctx.input_interval.str() << "\n" << "rate-status " << _ctx.prompt.rate.str() << "\n" << "rate-refresh " << _ctx.refresh_rate.str() << "\n" << "block " << _peaclock.cfg.x_block.str() << " " << _peaclock.cfg.y_block.str() << "\n" << "padding " << _peaclock.cfg.x_space.str() << " " << _peaclock.cfg.y_space.str() << "\n" << "margin " << _peaclock.cfg.x_border.str() << " " << _peaclock.cfg.y_border.str() << "\n" << "ratio " << _peaclock.cfg.x_ratio.str() << " " << _peaclock.cfg.y_ratio.str() << "\n" << "date-padding " << _peaclock.cfg.date_padding.str() << "\n" << "set date " << btos(_peaclock.cfg.date) << "\n" << "set seconds " << btos(_peaclock.cfg.seconds) << "\n" << "set hour-24 " << btos(_peaclock.cfg.hour_24) << "\n" << "set auto-size " << btos(_peaclock.cfg.auto_size) << "\n" << "set auto-ratio " << btos(_peaclock.cfg.auto_ratio) << "\n" << "style active-fg " << _peaclock.cfg.style.active_fg.key() << "\n" << "style active-bg " << _peaclock.cfg.style.active_bg.key() << "\n" << "style inactive-fg " << _peaclock.cfg.style.inactive_fg.key() << "\n" << "style inactive-bg " << _peaclock.cfg.style.inactive_bg.key() << "\n" << "style colon-fg " << _peaclock.cfg.style.colon_fg.key() << "\n" << "style colon-bg " << _peaclock.cfg.style.colon_bg.key() << "\n" << "style date " << _peaclock.cfg.style.date.key() << "\n" << "style text " << _ctx.style.text.key() << "\n" << "style background " << _ctx.style.background.key() << "\n" << "style prompt " << _ctx.style.prompt.key() << "\n" << "style success " << _ctx.style.success.key() << "\n" << "style error " << _ctx.style.error.key() << "\n" << std::flush; set_status(true, "config saved to '" + fpath.lexically_normal().string() + "'"); return true; } void Tui::run() { std::cout << aec::cursor_hide << aec::screen_push << aec::cursor_hide << aec::screen_clear << aec::cursor_home << aec::mouse_enable << std::flush; // set terminal mode to raw _term_mode.set_min(0); _term_mode.set_raw(); // start the event loop event_loop(); std::cout << aec::mouse_disable << aec::nl << aec::screen_pop << aec::cursor_show << std::flush; } void Tui::event_loop() { while (_ctx.is_running) { // get the terminal width and height OB::Term::size(_ctx.width, _ctx.height); // check for correct screen size if (screen_size() != 0) { std::this_thread::sleep_for(std::chrono::milliseconds(_ctx.input_interval)); char32_t key {0}; if ((key = OB::Term::get_key()) > 0) { switch (key) { case 'q': case 'Q': case OB::Term::ctrl_key('c'): { _ctx.is_running = false; break; } default: { break; } } } continue; } // render new content clear(); draw(); refresh(); int wait {_ctx.refresh_rate.get()}; int tick {0}; while (_ctx.is_running && wait) { if (wait > _ctx.input_interval.get()) { tick = _ctx.input_interval.get(); wait -= _ctx.input_interval.get(); } else { tick = wait; wait = 0; } std::this_thread::sleep_for(std::chrono::milliseconds(tick)); get_input(); } } } void Tui::clear() { // clear screen _ctx.buf << aec::cursor_home << _ctx.style.background; OB::Algorithm::for_each(_ctx.height, [&](auto) { OB::Algorithm::for_each(_ctx.width, [&](auto) { _ctx.buf << " "; }); _ctx.buf << "\n"; }, [&](auto) { OB::Algorithm::for_each(_ctx.width, [&](auto) { _ctx.buf << " "; }); }); _ctx.buf << aec::clear << aec::cursor_home; } void Tui::refresh() { // output buffer to screen std::cout << _ctx.buf.str() << std::flush; // clear output buffer _ctx.buf.str(""); } void Tui::draw() { draw_content(); draw_prompt_message(); draw_keybuf(); } void Tui::draw_content() { _ctx.buf << aec::cursor_save; // timer if (! _peaclock.timer && _peaclock.cfg.timer_notify) { _peaclock.cfg.timer_notify = false; if (! _peaclock.cfg.timer_exec.empty()) { std::thread notify {[cmd = _peaclock.cfg.timer_exec]() { std::system(cmd.c_str()); }}; notify.detach(); } } // render new content _peaclock.render(_ctx.width, _ctx.height, _ctx.buf); _ctx.buf << aec::clear << aec::cursor_load; } void Tui::draw_keybuf() { if (_ctx.keys.empty()) { return; } _ctx.buf << aec::cursor_save << aec::cursor_set(_ctx.width - 3, _ctx.height) << _ctx.style.background << " " << aec::cursor_set(_ctx.width - 3, _ctx.height) << _ctx.style.text << aec::space; for (auto const& e : _ctx.keys) { if (OB::Text::is_print(static_cast(e.val))) { _ctx.buf << e.str; } } _ctx.buf << aec::space << aec::clear << aec::cursor_load; } void Tui::draw_prompt_message() { // check if command prompt message is active if (_ctx.prompt.count > 0) { --_ctx.prompt.count; _ctx.buf << aec::cursor_save << aec::cursor_set(0, _ctx.height) << _ctx.style.background << _ctx.style.prompt << ">" << _ctx.style.prompt_status << _ctx.prompt.str.substr(0, _ctx.width - 5) << aec::cursor_load; } } void Tui::set_status(bool success, std::string const& msg) { _ctx.style.prompt_status = success ? _ctx.style.success : _ctx.style.error; _ctx.prompt.str = msg; _ctx.prompt.count = _ctx.prompt.timeout; } void Tui::get_input() { if ((_ctx.key.val = OB::Term::get_key(&_ctx.key.str)) > 0) { _ctx.keys.emplace_back(_ctx.key); switch (_ctx.keys.at(0).val) { // quit case 'q': case 'Q': { _ctx.is_running = false; _ctx.keys.clear(); return; } case OB::Term::ctrl_key('c'): { _ctx.is_running = false; _ctx.keys.clear(); return; } case OB::Term::Key::escape: { _ctx.prompt.count = 0; _ctx.keys.clear(); break; } // command prompt case ':': { command_prompt(); _ctx.keys.clear(); break; } case 'a': { _peaclock.cfg.hour_24 = ! _peaclock.cfg.hour_24; set_status(true, "set hour-24 "s + btos(_peaclock.cfg.hour_24)); break; } case 's': { _peaclock.cfg.seconds = ! _peaclock.cfg.seconds; set_status(true, "set seconds "s + btos(_peaclock.cfg.seconds)); break; } case 'd': { _peaclock.cfg.date = ! _peaclock.cfg.date; set_status(true, "set date "s + btos(_peaclock.cfg.date)); break; } case 'f': { _peaclock.cfg.auto_size = ! _peaclock.cfg.auto_size; set_status(true, "set auto-size "s + btos(_peaclock.cfg.auto_size)); break; } case 'g': { _peaclock.cfg.auto_ratio = ! _peaclock.cfg.auto_ratio; set_status(true, "set auto-ratio "s + btos(_peaclock.cfg.auto_ratio)); break; } case 'h': { switch (_peaclock.cfg.toggle) { case Peaclock::Toggle::block: { --_peaclock.cfg.x_block; set_status(true, "block-x " + _peaclock.cfg.x_block.str()); break; } case Peaclock::Toggle::padding: { --_peaclock.cfg.x_space; set_status(true, "padding-x " + _peaclock.cfg.x_space.str()); break; } case Peaclock::Toggle::margin: { --_peaclock.cfg.x_border; set_status(true, "margin-x " + _peaclock.cfg.x_border.str()); break; } case Peaclock::Toggle::ratio: { --_peaclock.cfg.x_ratio; set_status(true, "ratio-x " + _peaclock.cfg.x_ratio.str()); break; } case Peaclock::Toggle::active_fg: { _peaclock.cfg.style.active_fg.hue(_peaclock.cfg.style.active_fg.hue() - 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.active_fg.hue())); break; } case Peaclock::Toggle::active_bg: { _peaclock.cfg.style.active_bg.hue(_peaclock.cfg.style.active_bg.hue() - 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.active_bg.hue())); break; } case Peaclock::Toggle::inactive_fg: { _peaclock.cfg.style.inactive_fg.hue(_peaclock.cfg.style.inactive_fg.hue() - 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.inactive_fg.hue())); break; } case Peaclock::Toggle::inactive_bg: { _peaclock.cfg.style.inactive_bg.hue(_peaclock.cfg.style.inactive_bg.hue() - 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.inactive_bg.hue())); break; } case Peaclock::Toggle::colon_fg: { _peaclock.cfg.style.colon_fg.hue(_peaclock.cfg.style.colon_fg.hue() - 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.colon_fg.hue())); break; } case Peaclock::Toggle::colon_bg: { _peaclock.cfg.style.colon_bg.hue(_peaclock.cfg.style.colon_bg.hue() - 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.colon_bg.hue())); break; } case Peaclock::Toggle::date: { _peaclock.cfg.style.date.hue(_peaclock.cfg.style.date.hue() - 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.date.hue())); break; } case Peaclock::Toggle::background: { _ctx.style.background.hue(_ctx.style.background.hue() - 0.5); _peaclock.cfg.style.background.hue(_peaclock.cfg.style.background.hue() - 0.5); set_status(true, "hue " + OB::String::to_string(_ctx.style.background.hue())); break; } default: { break; } } break; } case 'j': { switch (_peaclock.cfg.toggle) { case Peaclock::Toggle::block: { --_peaclock.cfg.y_block; set_status(true, "block-y " + _peaclock.cfg.y_block.str()); break; } case Peaclock::Toggle::padding: { --_peaclock.cfg.y_space; set_status(true, "padding-y " + _peaclock.cfg.y_space.str()); break; } case Peaclock::Toggle::margin: { --_peaclock.cfg.y_border; set_status(true, "margin-y " + _peaclock.cfg.y_border.str()); break; } case Peaclock::Toggle::ratio: { --_peaclock.cfg.y_ratio; set_status(true, "ratio-y " + _peaclock.cfg.y_ratio.str()); break; } case Peaclock::Toggle::active_fg: { _peaclock.cfg.style.active_fg.sat(_peaclock.cfg.style.active_fg.sat() - 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.active_fg.sat())); break; } case Peaclock::Toggle::active_bg: { _peaclock.cfg.style.active_bg.sat(_peaclock.cfg.style.active_bg.sat() - 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.active_bg.sat())); break; } case Peaclock::Toggle::inactive_fg: { _peaclock.cfg.style.inactive_fg.sat(_peaclock.cfg.style.inactive_fg.sat() - 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.inactive_fg.sat())); break; } case Peaclock::Toggle::inactive_bg: { _peaclock.cfg.style.inactive_bg.sat(_peaclock.cfg.style.inactive_bg.sat() - 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.inactive_bg.sat())); break; } case Peaclock::Toggle::colon_fg: { _peaclock.cfg.style.colon_fg.sat(_peaclock.cfg.style.colon_fg.sat() - 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.colon_fg.sat())); break; } case Peaclock::Toggle::colon_bg: { _peaclock.cfg.style.colon_bg.sat(_peaclock.cfg.style.colon_bg.sat() - 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.colon_bg.sat())); break; } case Peaclock::Toggle::date: { _peaclock.cfg.style.date.sat(_peaclock.cfg.style.date.sat() - 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.date.sat())); break; } case Peaclock::Toggle::background: { _ctx.style.background.sat(_ctx.style.background.sat() - 0.5); _peaclock.cfg.style.background.sat(_peaclock.cfg.style.background.sat() - 0.5); set_status(true, "sat " + OB::String::to_string(_ctx.style.background.sat())); break; } default: { break; } } break; } case 'k': { switch (_peaclock.cfg.toggle) { case Peaclock::Toggle::block: { ++_peaclock.cfg.y_block; set_status(true, "block-y " + _peaclock.cfg.y_block.str()); break; } case Peaclock::Toggle::padding: { ++_peaclock.cfg.y_space; set_status(true, "padding-y " + _peaclock.cfg.y_space.str()); break; } case Peaclock::Toggle::margin: { ++_peaclock.cfg.y_border; set_status(true, "margin-y " + _peaclock.cfg.y_border.str()); break; } case Peaclock::Toggle::ratio: { ++_peaclock.cfg.y_ratio; set_status(true, "ratio-y " + _peaclock.cfg.y_ratio.str()); break; } case Peaclock::Toggle::active_fg: { _peaclock.cfg.style.active_fg.sat(_peaclock.cfg.style.active_fg.sat() + 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.active_fg.sat())); break; } case Peaclock::Toggle::active_bg: { _peaclock.cfg.style.active_bg.sat(_peaclock.cfg.style.active_bg.sat() + 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.active_bg.sat())); break; } case Peaclock::Toggle::inactive_fg: { _peaclock.cfg.style.inactive_fg.sat(_peaclock.cfg.style.inactive_fg.sat() + 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.inactive_fg.sat())); break; } case Peaclock::Toggle::inactive_bg: { _peaclock.cfg.style.inactive_bg.sat(_peaclock.cfg.style.inactive_bg.sat() + 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.inactive_bg.sat())); break; } case Peaclock::Toggle::colon_fg: { _peaclock.cfg.style.colon_fg.sat(_peaclock.cfg.style.colon_fg.sat() + 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.colon_fg.sat())); break; } case Peaclock::Toggle::colon_bg: { _peaclock.cfg.style.colon_bg.sat(_peaclock.cfg.style.colon_bg.sat() + 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.colon_bg.sat())); break; } case Peaclock::Toggle::date: { _peaclock.cfg.style.date.sat(_peaclock.cfg.style.date.sat() + 0.5); set_status(true, "sat " + OB::String::to_string(_peaclock.cfg.style.date.sat())); break; } case Peaclock::Toggle::background: { _ctx.style.background.sat(_ctx.style.background.sat() + 0.5); _peaclock.cfg.style.background.sat(_peaclock.cfg.style.background.sat() + 0.5); set_status(true, "sat " + OB::String::to_string(_ctx.style.background.sat())); break; } default: { break; } } break; } case 'l': { switch (_peaclock.cfg.toggle) { case Peaclock::Toggle::block: { ++_peaclock.cfg.x_block; set_status(true, "block-x " + _peaclock.cfg.x_block.str()); break; } case Peaclock::Toggle::padding: { ++_peaclock.cfg.x_space; set_status(true, "padding-x " + _peaclock.cfg.x_space.str()); break; } case Peaclock::Toggle::margin: { ++_peaclock.cfg.x_border; set_status(true, "margin-x " + _peaclock.cfg.x_border.str()); break; } case Peaclock::Toggle::ratio: { ++_peaclock.cfg.x_ratio; set_status(true, "ratio-x " + _peaclock.cfg.x_ratio.str()); break; } case Peaclock::Toggle::active_fg: { _peaclock.cfg.style.active_fg.hue(_peaclock.cfg.style.active_fg.hue() + 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.active_fg.hue())); break; } case Peaclock::Toggle::active_bg: { _peaclock.cfg.style.active_bg.hue(_peaclock.cfg.style.active_bg.hue() + 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.active_bg.hue())); break; } case Peaclock::Toggle::inactive_fg: { _peaclock.cfg.style.inactive_fg.hue(_peaclock.cfg.style.inactive_fg.hue() + 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.inactive_fg.hue())); break; } case Peaclock::Toggle::inactive_bg: { _peaclock.cfg.style.inactive_bg.hue(_peaclock.cfg.style.inactive_bg.hue() + 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.inactive_bg.hue())); break; } case Peaclock::Toggle::colon_fg: { _peaclock.cfg.style.colon_fg.hue(_peaclock.cfg.style.colon_fg.hue() + 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.colon_fg.hue())); break; } case Peaclock::Toggle::colon_bg: { _peaclock.cfg.style.colon_bg.hue(_peaclock.cfg.style.colon_bg.hue() + 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.colon_bg.hue())); break; } case Peaclock::Toggle::date: { _peaclock.cfg.style.date.hue(_peaclock.cfg.style.date.hue() + 0.5); set_status(true, "hue " + OB::String::to_string(_peaclock.cfg.style.date.hue())); break; } case Peaclock::Toggle::background: { _ctx.style.background.hue(_ctx.style.background.hue() + 0.5); _peaclock.cfg.style.background.hue(_peaclock.cfg.style.background.hue() + 0.5); set_status(true, "hue " + OB::String::to_string(_ctx.style.background.hue())); break; } default: { break; } } break; } case ';': { switch (_peaclock.cfg.toggle) { case Peaclock::Toggle::active_fg: { _peaclock.cfg.style.active_fg.lum(_peaclock.cfg.style.active_fg.lum() - 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.active_fg.lum())); break; } case Peaclock::Toggle::active_bg: { _peaclock.cfg.style.active_bg.lum(_peaclock.cfg.style.active_bg.lum() - 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.active_bg.lum())); break; } case Peaclock::Toggle::inactive_fg: { _peaclock.cfg.style.inactive_fg.lum(_peaclock.cfg.style.inactive_fg.lum() - 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.inactive_fg.lum())); break; } case Peaclock::Toggle::inactive_bg: { _peaclock.cfg.style.inactive_bg.lum(_peaclock.cfg.style.inactive_bg.lum() - 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.inactive_bg.lum())); break; } case Peaclock::Toggle::colon_fg: { _peaclock.cfg.style.colon_fg.lum(_peaclock.cfg.style.colon_fg.lum() - 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.colon_fg.lum())); break; } case Peaclock::Toggle::colon_bg: { _peaclock.cfg.style.colon_bg.lum(_peaclock.cfg.style.colon_bg.lum() - 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.colon_bg.lum())); break; } case Peaclock::Toggle::date: { _peaclock.cfg.style.date.lum(_peaclock.cfg.style.date.lum() - 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.date.lum())); break; } case Peaclock::Toggle::background: { _ctx.style.background.lum(_ctx.style.background.lum() - 0.5); _peaclock.cfg.style.background.lum(_peaclock.cfg.style.background.lum() - 0.5); set_status(true, "lum " + OB::String::to_string(_ctx.style.background.lum())); break; } default: { break; } } break; } case '\'': { switch (_peaclock.cfg.toggle) { case Peaclock::Toggle::active_fg: { _peaclock.cfg.style.active_fg.lum(_peaclock.cfg.style.active_fg.lum() + 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.active_fg.lum())); break; } case Peaclock::Toggle::active_bg: { _peaclock.cfg.style.active_bg.lum(_peaclock.cfg.style.active_bg.lum() + 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.active_bg.lum())); break; } case Peaclock::Toggle::inactive_fg: { _peaclock.cfg.style.inactive_fg.lum(_peaclock.cfg.style.inactive_fg.lum() + 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.inactive_fg.lum())); break; } case Peaclock::Toggle::inactive_bg: { _peaclock.cfg.style.inactive_bg.lum(_peaclock.cfg.style.inactive_bg.lum() + 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.inactive_bg.lum())); break; } case Peaclock::Toggle::date: { _peaclock.cfg.style.date.lum(_peaclock.cfg.style.date.lum() + 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.date.lum())); break; } case Peaclock::Toggle::colon_fg: { _peaclock.cfg.style.colon_fg.lum(_peaclock.cfg.style.colon_fg.lum() + 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.colon_fg.lum())); break; } case Peaclock::Toggle::colon_bg: { _peaclock.cfg.style.colon_bg.lum(_peaclock.cfg.style.colon_bg.lum() + 0.5); set_status(true, "lum " + OB::String::to_string(_peaclock.cfg.style.colon_bg.lum())); break; } case Peaclock::Toggle::background: { _ctx.style.background.lum(_ctx.style.background.lum() + 0.5); _peaclock.cfg.style.background.lum(_peaclock.cfg.style.background.lum() + 0.5); set_status(true, "lum " + OB::String::to_string(_ctx.style.background.lum())); break; } default: { break; } } break; } case '/': { switch (_peaclock.cfg.toggle) { case Peaclock::Toggle::active_fg: { _peaclock.cfg.style.active_fg.key("clear"); set_status(true, "active-fg clear"); break; } case Peaclock::Toggle::active_bg: { _peaclock.cfg.style.active_bg.key("clear"); set_status(true, "active-bg clear"); break; } case Peaclock::Toggle::inactive_fg: { _peaclock.cfg.style.inactive_fg.key("clear"); set_status(true, "inactive-fg clear"); break; } case Peaclock::Toggle::inactive_bg: { _peaclock.cfg.style.inactive_bg.key("clear"); set_status(true, "inactive-bg clear"); break; } case Peaclock::Toggle::colon_fg: { _peaclock.cfg.style.colon_fg.key("clear"); set_status(true, "colon-fg clear"); break; } case Peaclock::Toggle::colon_bg: { _peaclock.cfg.style.colon_bg.key("clear"); set_status(true, "colon-bg clear"); break; } case Peaclock::Toggle::date: { _peaclock.cfg.style.date.key("clear"); set_status(true, "date clear"); break; } case Peaclock::Toggle::background: { _ctx.style.background.key("clear"); _peaclock.cfg.style.background.key("clear"); set_status(true, "background clear"); break; } default: { break; } } break; } case 'p': { _peaclock.cfg.toggle = Peaclock::Toggle::block; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'o': { _peaclock.cfg.toggle = Peaclock::Toggle::padding; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'i': { _peaclock.cfg.toggle = Peaclock::Toggle::margin; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'u': { _peaclock.cfg.toggle = Peaclock::Toggle::ratio; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'x': { _peaclock.cfg.toggle = Peaclock::Toggle::active_fg; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'c': { _peaclock.cfg.toggle = Peaclock::Toggle::inactive_fg; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'v': { _peaclock.cfg.toggle = Peaclock::Toggle::colon_fg; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'b': { _peaclock.cfg.toggle = Peaclock::Toggle::active_bg; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'n': { _peaclock.cfg.toggle = Peaclock::Toggle::inactive_bg; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'm': { _peaclock.cfg.toggle = Peaclock::Toggle::colon_bg; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case '.': { _peaclock.cfg.toggle = Peaclock::Toggle::background; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case ',': { _peaclock.cfg.toggle = Peaclock::Toggle::date; set_status(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); break; } case 'w': { _peaclock.cfg.mode = Peaclock::Mode::clock; set_status(true, "mode " + Peaclock::Mode::str(_peaclock.cfg.mode)); break; } case 'e': { _peaclock.cfg.mode = Peaclock::Mode::timer; set_status(true, "mode " + Peaclock::Mode::str(_peaclock.cfg.mode)); break; } case 'r': { _peaclock.cfg.mode = Peaclock::Mode::stopwatch; set_status(true, "mode " + Peaclock::Mode::str(_peaclock.cfg.mode)); break; } case 'W': { _peaclock.cfg.date = true; _peaclock.cfg.view = Peaclock::View::date; set_status(true, "view " + Peaclock::View::str(_peaclock.cfg.view)); break; } case 'E': { _peaclock.cfg.view = Peaclock::View::ascii; set_status(true, "view " + Peaclock::View::str(_peaclock.cfg.view)); break; } case 'R': { _peaclock.cfg.view = Peaclock::View::digital; set_status(true, "view " + Peaclock::View::str(_peaclock.cfg.view)); break; } case 'T': { _peaclock.cfg.view = Peaclock::View::binary; set_status(true, "view " + Peaclock::View::str(_peaclock.cfg.view)); break; } case 'Y': { _peaclock.cfg.view = Peaclock::View::icon; set_status(true, "view " + Peaclock::View::str(_peaclock.cfg.view)); break; } case ' ': { switch (_peaclock.cfg.mode) { case Peaclock::Mode::timer: { if (_peaclock.timer.seconds() >= _peaclock.cfg.timer_seconds) { _peaclock.timer.reset(); _peaclock.cfg.timer_notify = false; set_status(true, "timer clear"); } else { _peaclock.timer.toggle(); set_status(true, "timer "s + (_peaclock.timer ? "start" : "stop")); } break; } case Peaclock::Mode::stopwatch: { _peaclock.stopwatch.toggle(); set_status(true, "stopwatch "s + (_peaclock.stopwatch ? "start" : "stop")); break; } default: { break; } } break; } case OB::Term::Key::backspace: { switch (_peaclock.cfg.mode) { case Peaclock::Mode::timer: { _peaclock.timer.reset(); _peaclock.cfg.timer_notify = false; set_status(true, "timer clear"); break; } case Peaclock::Mode::stopwatch: { _peaclock.stopwatch.reset(); set_status(true, "stopwatch clear"); break; } default: { break; } } break; } default: { // ignore draw_keybuf(); refresh(); _ctx.keys.clear(); return; } } clear(); draw(); refresh(); _ctx.keys.clear(); } while (OB::Term::get_key(&_ctx.key.str) > 0); } std::optional> Tui::command(std::string const& input) { // quit if (! _ctx.is_running) { _ctx.is_running = false; return {}; } // nop if (input.empty()) { return {}; } auto const keys = OB::String::split(input, " ", 2); if (keys.empty()) { return {}; } // store the matches returned from OB::String::match std::optional> match_opt; // quit if (keys.size() == 1 && (keys.at(0) == "q" || keys.at(0) == "Q" || keys.at(0) == "quit" || keys.at(0) == "Quit" || keys.at(0) == "exit")) { _ctx.is_running = false; return {}; } else if (keys.at(0) == "h" || keys.at(0) == "help") { std::cout << aec::mouse_disable << aec::nl << aec::screen_pop << aec::cursor_show << std::flush; _term_mode.set_cooked(); std::system(("$(which less) -ir" + (keys.size() > 1 ? " '+/" + keys.at(1) + (keys.size() > 2 ? " " + keys.at(2) : "") + "'" : "") + " <<'EOF'\n" + _pg.help() + "EOF").c_str()); _term_mode.set_raw(); std::cout << aec::cursor_hide << aec::screen_push << aec::cursor_hide << aec::screen_clear << aec::cursor_home << aec::mouse_enable << std::flush; } else if (keys.at(0) == "timer" && (match_opt = OB::String::match(input, std::regex("^timer(?:\\s+(clear|start|stop|(?:(?:\\d+Y)?:?(?:\\d+M)?:?(?:\\d+W)?:?(?:\\d+D)?:?(?:\\d+h)?:?(?:\\d+m)?:?(?:\\d+s)?)))?$")))) { auto const match = OB::String::trim(match_opt.value().at(1)); if (match.empty()) { return std::make_pair(true, "timer " + OB::Timer::sec_to_str(_peaclock.cfg.timer_seconds)); // return std::make_pair(true, "timer " + OB::Timer::sec_to_str(_peaclock.cfg.timer_seconds - _peaclock.timer.seconds())); } if (match == "clear") { _peaclock.timer.reset(); _peaclock.cfg.timer_notify = false; } else if (match == "start") { if (_peaclock.timer.seconds() >= _peaclock.cfg.timer_seconds) { _peaclock.timer.reset(); _peaclock.cfg.timer_notify = false; } _peaclock.timer.start(); } else if (match == "stop") { _peaclock.timer.stop(); } else { _peaclock.timer.reset(); _peaclock.cfg.timer_notify = false; _peaclock.cfg.timer_seconds = OB::Timer::str_to_sec(match); } } else if (keys.at(0) == "stopwatch" && (match_opt = OB::String::match(input, std::regex("^stopwatch(?:\\s+(clear|start|stop|(?:(?:\\d+Y)?:?(?:\\d+M)?:?(?:\\d+W)?:?(?:\\d+D)?:?(?:\\d+h)?:?(?:\\d+m)?:?(?:\\d+s)?)))?$")))) { auto const match = OB::String::trim(match_opt.value().at(1)); if (match.empty()) { return std::make_pair(true, "stopwatch " + _peaclock.stopwatch.str()); } if (match == "clear") { _peaclock.stopwatch.reset(); } else if (match == "start") { _peaclock.stopwatch.start(); } else if (match == "stop") { _peaclock.stopwatch.stop(); } else { _peaclock.stopwatch.str(match); } } else if (keys.at(0) == "timer-exec" && (match_opt = OB::String::match(input, std::regex("^timer-exec(?:\\s+(?:(" + _ctx.rx.str + ")))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "timer-exec '" + OB::String::escape(_peaclock.cfg.timer_exec) + "'"); } else { if (match.size() == 2) { _peaclock.cfg.timer_exec = ""; } else { _peaclock.cfg.timer_exec = OB::String::unescape(match.substr(1, match.size() - 2)); } } } else if (keys.at(0) == "mkconfig" || keys.at(0) == "mkconfig!") { if (keys.size() == 2) { mkconfig(keys.at(1), keys.at(0).back() == '!'); } else { return std::make_pair(false, "error: expected output file path"); } } else if (keys.at(0) == "rate-input" && (match_opt = OB::String::match(input, std::regex("^rate-input(?:\\s+([0-9]+))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "rate-input " + _ctx.input_interval.str()); } else { _ctx.input_interval = std::stoi(match); } } else if (keys.at(0) == "rate-refresh" && (match_opt = OB::String::match(input, std::regex("^rate-refresh(?:\\s+([0-9]+))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "rate-refresh " + _ctx.refresh_rate.str()); } else { _ctx.refresh_rate = std::stoi(match); _ctx.prompt.timeout = _ctx.prompt.rate.get() / _ctx.refresh_rate.get(); } } else if (keys.at(0) == "rate-status" && (match_opt = OB::String::match(input, std::regex("^rate-status(?:\\s+([0-9]+))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "rate-status " + _ctx.prompt.rate.str()); } else { _ctx.prompt.rate = std::stoi(match); _ctx.prompt.timeout = _ctx.prompt.rate.get() / _ctx.refresh_rate.get(); } } else if (keys.at(0) == "locale" && (match_opt = OB::String::match(input, std::regex("^locale(?:\\s+(?:(" + _ctx.rx.str + ")))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "locale '" + _peaclock.cfg.locale + "'"); } else { if (match.size() == 2) { _peaclock.cfg_locale(""); } else if (! _peaclock.cfg_locale(match.substr(1, match.size() - 2))) { return std::make_pair(false, "error: invalid locale '" + match.substr(1, match.size() - 2) + "'"); } } } else if (keys.at(0) == "timezone" && (match_opt = OB::String::match(input, std::regex("^timezone(?:\\s+(?:(" + _ctx.rx.str + ")))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "timezone '" + _peaclock.cfg.timezone + "'"); } else { if (match.size() == 2) { _peaclock.cfg_timezone(""); } else if (! _peaclock.cfg_timezone(match.substr(1, match.size() - 2))) { return std::make_pair(false, "error: invalid timezone '" + match.substr(1, match.size() - 2) + "'"); } } } else if (keys.at(0) == "date" && (match_opt = OB::String::match(input, std::regex("^date(?:\\s+(?:(" + _ctx.rx.str + ")))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "date '" + OB::String::escape(_peaclock.cfg.datefmt) + "'"); } else { if (match.size() == 2) { _peaclock.cfg_datefmt(""); } else { _peaclock.cfg_datefmt(OB::String::unescape(match.substr(1, match.size() - 2))); } } } else if (keys.at(0) == "fill" && (match_opt = OB::String::match(input, std::regex("^fill(?:\\s+(?:(" + _ctx.rx.str + ")))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "fill '" + OB::String::escape(_peaclock.cfg.fill_active) + "'"); } else { if (match.size() == 2) { _peaclock.cfg.fill_active = ""; _peaclock.cfg.fill_inactive = ""; _peaclock.cfg.fill_colon = ""; } else { _peaclock.cfg.fill_active = OB::String::unescape(match.substr(1, match.size() - 2)); _peaclock.cfg.fill_inactive = _peaclock.cfg.fill_active; _peaclock.cfg.fill_colon = _peaclock.cfg.fill_active; } } } else if (keys.at(0) == "fill-active" && (match_opt = OB::String::match(input, std::regex("^fill-active(?:\\s+(?:(" + _ctx.rx.str + ")))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "fill-active '" + OB::String::escape(_peaclock.cfg.fill_active) + "'"); } else { if (match.size() == 2) { _peaclock.cfg.fill_active = ""; } else { _peaclock.cfg.fill_active = OB::String::unescape(match.substr(1, match.size() - 2)); } } } else if (keys.at(0) == "fill-inactive" && (match_opt = OB::String::match(input, std::regex("^fill-inactive(?:\\s+(?:(" + _ctx.rx.str + ")))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "fill-inactive '" + OB::String::escape(_peaclock.cfg.fill_inactive) + "'"); } else { if (match.size() == 2) { _peaclock.cfg.fill_inactive = ""; } else { _peaclock.cfg.fill_inactive = OB::String::unescape(match.substr(1, match.size() - 2)); } } } else if (keys.at(0) == "fill-colon" && (match_opt = OB::String::match(input, std::regex("^fill-colon(?:\\s+(?:(" + _ctx.rx.str + ")))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "fill-colon '" + OB::String::escape(_peaclock.cfg.fill_colon) + "'"); } else { if (match.size() == 2) { _peaclock.cfg.fill_colon = ""; } else { _peaclock.cfg.fill_colon = OB::String::unescape(match.substr(1, match.size() - 2)); } } } else if (keys.at(0) == "mode" && (match_opt = OB::String::match(input, std::regex("^mode(?:\\s+(clock|timer|stopwatch))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "mode " + Peaclock::Mode::str(_peaclock.cfg.mode)); } else { _peaclock.cfg.mode = Peaclock::Mode::enm(match); } } else if (keys.at(0) == "view" && (match_opt = OB::String::match(input, std::regex("^view(?:\\s+(date|ascii|digital|binary|icon))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "view " + Peaclock::View::str(_peaclock.cfg.view)); } else { _peaclock.cfg.view = Peaclock::View::enm(match); } } else if (keys.at(0) == "toggle" && (match_opt = OB::String::match(input, std::regex("^toggle(?:\\s+(block|padding|margin|ratio|active-fg|inactive-fg|colon-fg|active-bg|inactive-bg|colon-bg|date|background))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "toggle " + Peaclock::Toggle::str(_peaclock.cfg.toggle)); } else { _peaclock.cfg.toggle = Peaclock::Toggle::enm(match); } } else if (keys.at(0) == "date-padding" && (match_opt = OB::String::match(input, std::regex("^date-padding(?:\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); if (x.empty()) { return std::make_pair(true, "date-padding " + _peaclock.cfg.date_padding.str()); } else { _peaclock.cfg.date_padding = std::stoul(x); } } else if (keys.at(0) == "block" && (match_opt = OB::String::match(input, std::regex("^block(?:\\s+([0-9]+)\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); auto const y = match_opt.value().at(2); if (x.empty() && y.empty()) { return std::make_pair(true, "block " + _peaclock.cfg.x_block.str() + " " + _peaclock.cfg.y_block.str()); } else { _peaclock.cfg.x_block = std::stoul(x); _peaclock.cfg.y_block = std::stoul(y); } } else if (keys.at(0) == "block-x" && (match_opt = OB::String::match(input, std::regex("^block-x(?:\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); if (x.empty()) { return std::make_pair(true, "block-x " + _peaclock.cfg.x_block.str()); } else { _peaclock.cfg.x_block = std::stoul(x); } } else if (keys.at(0) == "block-y" && (match_opt = OB::String::match(input, std::regex("^block-y(?:\\s+([0-9]+))?$")))) { auto const y = match_opt.value().at(1); if (y.empty()) { return std::make_pair(true, "block-y " + _peaclock.cfg.y_block.str()); } else { _peaclock.cfg.y_block = std::stoul(y); } } else if (keys.at(0) == "padding" && (match_opt = OB::String::match(input, std::regex("^padding(?:\\s+([0-9]+)\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); auto const y = match_opt.value().at(2); if (x.empty() && y.empty()) { return std::make_pair(true, "padding " + _peaclock.cfg.x_space.str() + " " + _peaclock.cfg.y_space.str()); } else { _peaclock.cfg.x_space = std::stoul(x); _peaclock.cfg.y_space = std::stoul(y); } } else if (keys.at(0) == "padding-x" && (match_opt = OB::String::match(input, std::regex("^padding-x(?:\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); if (x.empty()) { return std::make_pair(true, "padding-x " + _peaclock.cfg.x_space.str()); } else { _peaclock.cfg.x_space = std::stoul(x); } } else if (keys.at(0) == "padding-y" && (match_opt = OB::String::match(input, std::regex("^padding-y(?:\\s+([0-9]+))?$")))) { auto const y = match_opt.value().at(1); if (y.empty()) { return std::make_pair(true, "padding-y " + _peaclock.cfg.y_space.str()); } else { _peaclock.cfg.y_space = std::stoul(y); } } else if (keys.at(0) == "margin" && (match_opt = OB::String::match(input, std::regex("^margin(?:\\s+([0-9]+)\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); auto const y = match_opt.value().at(2); if (x.empty() && y.empty()) { return std::make_pair(true, "margin " + _peaclock.cfg.x_border.str() + " " + _peaclock.cfg.y_border.str()); } else { _peaclock.cfg.x_border = std::stoul(x); _peaclock.cfg.y_border = std::stoul(y); } } else if (keys.at(0) == "margin-x" && (match_opt = OB::String::match(input, std::regex("^margin-x(?:\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); if (x.empty()) { return std::make_pair(true, "margin-x " + _peaclock.cfg.x_border.str()); } else { _peaclock.cfg.x_border = std::stoul(x); } } else if (keys.at(0) == "margin-y" && (match_opt = OB::String::match(input, std::regex("^margin-y(?:\\s+([0-9]+))?$")))) { auto const y = match_opt.value().at(1); if (y.empty()) { return std::make_pair(true, "margin-y " + _peaclock.cfg.y_border.str()); } else { _peaclock.cfg.y_border = std::stoul(y); } } else if (keys.at(0) == "ratio" && (match_opt = OB::String::match(input, std::regex("^ratio(?:\\s+([0-9]+)\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); auto const y = match_opt.value().at(2); if (x.empty() && y.empty()) { return std::make_pair(true, "ratio " + _peaclock.cfg.x_ratio.str() + " " + _peaclock.cfg.y_ratio.str()); } else { _peaclock.cfg.x_ratio = std::stoul(x); _peaclock.cfg.y_ratio = std::stoul(y); } } else if (keys.at(0) == "ratio-x" && (match_opt = OB::String::match(input, std::regex("^ratio-x(?:\\s+([0-9]+))?$")))) { auto const x = match_opt.value().at(1); if (x.empty()) { return std::make_pair(true, "ratio-x " + _peaclock.cfg.x_ratio.str()); } else { _peaclock.cfg.x_ratio = std::stoul(x); } } else if (keys.at(0) == "ratio-y" && (match_opt = OB::String::match(input, std::regex("^ratio-y(?:\\s+([0-9]+))?$")))) { auto const y = match_opt.value().at(1); if (y.empty()) { return std::make_pair(true, "ratio-y " + _peaclock.cfg.y_ratio.str()); } else { _peaclock.cfg.y_ratio = std::stoul(y); } } else if (keys.size() >= 2 && keys.at(0) == "style") { if (keys.at(1) == "active-fg") { if (keys.size() < 3) { return std::make_pair(true, "style active-fg " + _peaclock.cfg.style.active_fg.key()); } OB::Color color {keys.at(2)}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _peaclock.cfg.style.active_fg = color; } else if (keys.at(1) == "active-bg") { if (keys.size() < 3) { return std::make_pair(true, "style active-bg " + _peaclock.cfg.style.active_bg.key()); } OB::Color color {keys.at(2), OB::Color::Type::bg}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _peaclock.cfg.style.active_bg = color; } else if (keys.at(1) == "inactive-fg") { if (keys.size() < 3) { return std::make_pair(true, "style inactive-fg " + _peaclock.cfg.style.inactive_fg.key()); } OB::Color color {keys.at(2)}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _peaclock.cfg.style.inactive_fg = color; } else if (keys.at(1) == "inactive-bg") { if (keys.size() < 3) { return std::make_pair(true, "style inactive-bg " + _peaclock.cfg.style.inactive_bg.key()); } OB::Color color {keys.at(2), OB::Color::Type::bg}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _peaclock.cfg.style.inactive_bg = color; } else if (keys.at(1) == "colon-fg") { if (keys.size() < 3) { return std::make_pair(true, "style colon-fg " + _peaclock.cfg.style.colon_fg.key()); } OB::Color color {keys.at(2)}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _peaclock.cfg.style.colon_fg = color; } else if (keys.at(1) == "colon-bg") { if (keys.size() < 3) { return std::make_pair(true, "style colon-bg " + _peaclock.cfg.style.colon_bg.key()); } OB::Color color {keys.at(2), OB::Color::Type::bg}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _peaclock.cfg.style.colon_bg = color; } else if (keys.at(1) == "date") { if (keys.size() < 3) { return std::make_pair(true, "style date " + _peaclock.cfg.style.date.key()); } OB::Color color {keys.at(2)}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _peaclock.cfg.style.date = color; } else if (keys.at(1) == "text") { if (keys.size() < 3) { return std::make_pair(true, "style text " + _ctx.style.text.key()); } OB::Color color {keys.at(2)}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _ctx.style.text = color; } else if (keys.at(1) == "background") { if (keys.size() < 3) { return std::make_pair(true, "style background " + _ctx.style.background.key()); } OB::Color color {keys.at(2), OB::Color::Type::bg}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _ctx.style.background = color; _peaclock.cfg.style.background = color; } else if (keys.at(1) == "prompt") { if (keys.size() < 3) { return std::make_pair(true, "style prompt " + _ctx.style.prompt.key()); } OB::Color color {keys.at(2)}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _ctx.style.prompt = color; } else if (keys.at(1) == "success") { if (keys.size() < 3) { return std::make_pair(true, "style success " + _ctx.style.success.key()); } OB::Color color {keys.at(2)}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _ctx.style.success = color; } else if (keys.at(1) == "error") { if (keys.size() < 3) { return std::make_pair(true, "style error " + _ctx.style.error.key()); } OB::Color color {keys.at(2)}; if (! color) { return std::make_pair(false, "warning: unknown command '" + input + "'"); } _ctx.style.error = color; } else { return std::make_pair(false, "warning: unknown command '" + input + "'"); } } else if (keys.size() >= 2 && keys.at(0) == "set") { if ((match_opt = OB::String::match(input, std::regex("^set\\s+date(?:\\s+(true|false|t|f|1|0|on|off))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "set date "s + btos(_peaclock.cfg.date)); } else if ("true" == match || "t" == match || "1" == match || "on" == match) { _peaclock.cfg.date = true; } else { _peaclock.cfg.date = false; } } else if ((match_opt = OB::String::match(input, std::regex("^set\\s+seconds(?:\\s+(true|false|t|f|1|0|on|off))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "set seconds "s + btos(_peaclock.cfg.seconds)); } else if ("true" == match || "t" == match || "1" == match || "on" == match) { _peaclock.cfg.seconds = true; } else { _peaclock.cfg.seconds = false; } } else if ((match_opt = OB::String::match(input, std::regex("^set\\s+hour-24(?:\\s+(true|false|t|f|1|0|on|off))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "set hour-24 "s + btos(_peaclock.cfg.hour_24)); } else if ("true" == match || "t" == match || "1" == match || "on" == match) { _peaclock.cfg.hour_24 = true; } else { _peaclock.cfg.hour_24 = false; } } else if ((match_opt = OB::String::match(input, std::regex("^set\\s+auto-size(?:\\s+(true|false|t|f|1|0|on|off))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "set auto-size "s + btos(_peaclock.cfg.auto_size)); } else if ("true" == match || "t" == match || "1" == match || "on" == match) { _peaclock.cfg.auto_size = true; } else { _peaclock.cfg.auto_size = false; } } else if ((match_opt = OB::String::match(input, std::regex("^set\\s+auto-ratio(?:\\s+(true|false|t|f|1|0|on|off))?$")))) { auto const match = match_opt.value().at(1); if (match.empty()) { return std::make_pair(true, "set auto-ratio "s + btos(_peaclock.cfg.auto_ratio)); } else if ("true" == match || "t" == match || "1" == match || "on" == match) { _peaclock.cfg.auto_ratio = true; } else { _peaclock.cfg.auto_ratio = false; } } else { return std::make_pair(false, "warning: unknown command '" + input + "'"); } } // unknown else { return std::make_pair(false, "warning: unknown command '" + input + "'"); } return {}; } void Tui::command_prompt() { // reset prompt message count _ctx.prompt.count = 0; // set prompt style _readline.style(_ctx.style.text.value() + _ctx.style.background.value()); _readline.prompt(":", _ctx.style.prompt.value() + _ctx.style.background.value()); std::cout << aec::cursor_save << aec::cursor_set(0, _ctx.height) << aec::erase_line << aec::cursor_show << std::flush; // read user input auto input = _readline(_ctx.is_running); std::cout << aec::cursor_hide << aec::cursor_load << std::flush; if (auto const res = command(input)) { set_status(res.value().first, res.value().second); } } int Tui::screen_size() { bool width_invalid {_ctx.width < _ctx.width_min}; bool height_invalid {_ctx.height < _ctx.height_min}; if (width_invalid || height_invalid) { clear(); _ctx.buf << _ctx.style.background << _ctx.style.error; if (width_invalid && height_invalid) { _ctx.buf << "Error: width " << _ctx.width << " (min " << _ctx.width_min << ") height " << _ctx.height << " (min " << _ctx.height_min << ")"; } else if (width_invalid) { _ctx.buf << "Error: width " << _ctx.width << " (min " << _ctx.width_min << ")"; } else { _ctx.buf << "Error: height " << _ctx.height << " (min " << _ctx.height_min << ")"; } _ctx.buf << aec::clear; refresh(); return 1; } return 0; }