138std::string 
const Screen::search_prompt = 
"Search: ";
 
  139std::string 
const Screen::regex_prompt  = 
"RegEx: ";
 
  141void LineEdit::move(LineEdit::movement dir)
 
  145      m_current_pos -= m_current_pos == 0 ? 0 : 1;
 
  148      m_current_pos += m_current_pos == m_current_line.size() ? 0 : 1;
 
  154      m_current_pos = m_current_line.size();
 
  157      if (not m_history.empty()) {
 
  158        if (m_history_pos == m_history.size()) {
 
  159          m_editing_line.swap(m_current_line);
 
  162        if (m_history_pos != 0) {
 
  166          std::advance(it, m_history_pos);
 
  168          m_current_line = *it;
 
  169          m_current_pos = m_current_line.size();
 
  174      if (not m_history.empty()) {
 
  175        if (m_history_pos == m_history.size() - 1) {
 
  176          m_editing_line.swap(m_current_line);
 
  178          m_current_pos = m_current_line.size();
 
  179        } 
else if (m_history_pos < m_history.size()) {
 
  183          std::advance(it, m_history_pos);
 
  185          m_current_line = *it;
 
  186          m_current_pos = m_current_line.size();
 
  193void LineEdit::load_history(std::string 
const& path)
 
  195  std::ifstream ifs(path.c_str());
 
  197  while (ifs && ifs.peek() != EOF) {
 
  199    std::getline(ifs, line);
 
  201    m_history.push_back(line);
 
  205void LineEdit::store_history(std::string 
const& path)
 const 
  207  std::ofstream ofs(path.c_str());
 
  210      citer it = m_history.begin(), et = m_history.end()
 
  218void Screen::elaborate(
int key)
 
  220  if (status != kSEARCH && status != kREGEX) {
 
  234        } 
else if (key == 
'c') {
 
  239        } 
else if (key == 
't') {
 
  242        } 
else if (key == 
'd') {
 
  245        } 
else if (key == 
'r') {
 
  248        } 
else if (key == 
'n') {
 
  251        } 
else if (key == 
'l') {
 
  254        } 
else if (key == 
'i') {
 
  255          m_sort_reverse = !m_sort_reverse;
 
  257        } 
else if (key == 
'g') {
 
  265        } 
else if (key == 
'G') {
 
  267            m_selected = m_list.size() - 1;
 
  273        } 
else if (key == 
'/') {
 
  275          m_search_editor.reset();
 
  276          update_bar(search_prompt, m_search_editor);
 
  277        } 
else if (key == 
'%') {
 
  279          m_regex_editor.reset();
 
  280          update_bar(regex_prompt, m_regex_editor);
 
  281        } 
else if (key == 
'm') {
 
  282          m_matching_first = ! m_matching_first;
 
  284          if (m_matching_first) {
 
  285            write_to_status_bar(
"Grouping matching CLBs on top");
 
  287            write_to_status_bar(
"Grouping disabled");
 
  289        } 
else if (key == 
'w') {
 
  292        } 
else if (key == kKEY_ENTER) {
 
  299        } 
else if (key == kKEY_DOWN) {
 
  302                m_selected + 1 == 
static_cast<int>(m_list.size())
 
  310        } 
else if (key == kKEY_UP) {
 
  321        } 
else if (key == kKEY_LEFT) {
 
  326        } 
else if (key == kKEY_RIGHT) {
 
  327          if (m_offset + COLS + 1 < max_line_size) {
 
  332          status_unrecognised(key);
 
  336        if (key == kKEY_ESC) {
 
  340          status_unrecognised(key);
 
  344        if (key == kKEY_ESC) {
 
  347        } 
else if (key == kKEY_DOWN) {
 
  350                m_selected + 1 == 
static_cast<int>(m_list.size())
 
  358        } 
else if (key == kKEY_UP) {
 
  370          status_unrecognised(key);
 
  374        if (key == kKEY_ESC) {
 
  377          if (!m_search_editor.current().empty()) {
 
  378            m_search_editor.add_history();
 
  385        } 
else if (key == kKEY_ENTER) {
 
  387          m_search_editor.add_history();
 
  391        } 
else if (key == kKEY_UP) {
 
  392          m_search_editor.move(LineEdit::HBACK);
 
  393        } 
else if (key == kKEY_DOWN) {
 
  394          m_search_editor.move(LineEdit::HFWD);
 
  395        } 
else if (key == kKEY_RIGHT) {
 
  396          m_search_editor.move(LineEdit::RIGHT);
 
  397        } 
else if (key == kKEY_LEFT) {
 
  398          m_search_editor.move(LineEdit::LEFT);
 
  399        } 
else if (key == kKEY_BACKS) {
 
  400          m_search_editor.remove_bwd();
 
  401        } 
else if (key == kKEY_DEL) {
 
  402          m_search_editor.remove_fwd();
 
  403        } 
else if (key == kKEY_HOME || key == kKEY_CTRLA) {
 
  404          m_search_editor.move(LineEdit::BEGIN);
 
  405        } 
else if (key == kKEY_END || key == kKEY_CTRLE) {
 
  406          m_search_editor.move(LineEdit::END);
 
  407        } 
else if (key == kKEY_CTRLL) {
 
  408          m_search_editor.reset();
 
  409        } 
else if (
ischar(key) && isprint(key)) {
 
  410          m_search_editor.append(key);
 
  414        update_bar(search_prompt, m_search_editor);
 
  419        if (key == kKEY_ESC) {
 
  423        } 
else if (key == kKEY_ENTER) {
 
  424          if (!m_regex_editor.current().empty()) {
 
  425            m_regex_editor.add_history();
 
  428          std::string 
const current_line = m_regex_editor.current();
 
  429          m_regex_editor.reset();
 
  434            m_regex.assign(current_line, boost::regex_constants::icase);
 
  438            write_to_status_bar(
"Invalid regex");
 
  443        } 
else if (key == kKEY_UP) {
 
  444          m_regex_editor.move(LineEdit::HBACK);
 
  445        } 
else if (key == kKEY_DOWN) {
 
  446          m_regex_editor.move(LineEdit::HFWD);
 
  447        } 
else if (key == kKEY_RIGHT) {
 
  448          m_regex_editor.move(LineEdit::RIGHT);
 
  449        } 
else if (key == kKEY_LEFT) {
 
  450          m_regex_editor.move(LineEdit::LEFT);
 
  451        } 
else if (key == kKEY_BACKS) {
 
  452          m_regex_editor.remove_bwd();
 
  453        } 
else if (key == kKEY_DEL) {
 
  454          m_regex_editor.remove_fwd();
 
  455        } 
else if (key == kKEY_HOME || key == kKEY_CTRLA) {
 
  456          m_regex_editor.move(LineEdit::BEGIN);
 
  457        } 
else if (key == kKEY_END || key == kKEY_CTRLE) {
 
  458          m_regex_editor.move(LineEdit::END);
 
  459        } 
else if (key == kKEY_CTRLL) {
 
  460          m_regex_editor.reset();
 
  461        } 
else if (
ischar(key) && isprint(key)) {
 
  462          m_regex_editor.append(key);
 
  466        update_bar(regex_prompt, m_regex_editor);
 
  469        assert(! 
"Internal screen error");
 
  473void Screen::update_bar(std::string 
const& prompt, LineEdit 
const& le)
 
  476  wprintw(m_twin, 
"%s", prompt.c_str());
 
  477  wprintw(m_twin, 
"%s", le.current().c_str());
 
  478  wmove(m_twin, 0, le.cursor() + prompt.size());
 
  479  wnoutrefresh(m_twin);
 
  483void Screen::show_help()
 const 
  489  int const lines = 16;
 
  490  int const x_pos = (COLS - cols) / 2;
 
  491  int const y_pos = (LINES - lines) / 2;
 
  492  WINDOW* help_win = newwin(lines, cols, y_pos, x_pos);
 
  494  wmove(help_win, 0, 6);
 
  495  wprintw(help_win, 
" Help ");
 
  496  wmove(help_win, 1, col1);
 
  497  wprintw(help_win, 
"Press the key to activate the corresponding behaviour.");
 
  498  wmove(help_win, 3, col1);
 
  499  wprintw(help_win, 
"Sort:");
 
  500  wmove(help_win, 4, col1);
 
  501  wprintw(help_win, 
"- t: sort by last view time");
 
  502  wmove(help_win, 5, col1);
 
  503  wprintw(help_win, 
"- d: sort by DOM ID");
 
  504  wmove(help_win, 6, col1);
 
  505  wprintw(help_win, 
"- r: sort by avg hit rate");
 
  506  wmove(help_win, 7, col1);
 
  507  wprintw(help_win, 
"- n: sort by run number");
 
  508  wmove(help_win, 8, col1);
 
  509  wprintw(help_win, 
"- l: sort by label (name)");
 
  510  wmove(help_win, 9, col1);
 
  511  wprintw(help_win, 
"- i: invert sorting");
 
  512  wmove(help_win, 3, col2);
 
  513  wprintw(help_win, 
"Screen:");
 
  514  wmove(help_win, 4, col2);
 
  515  wprintw(help_win, 
"- c: clear the screen");
 
  516  wmove(help_win, 5, col2);
 
  517  wprintw(help_win, 
"- Arrow keys: move selection/view");
 
  518  wmove(help_win, 6, col2);
 
  519  wprintw(help_win, 
"- g: move selection to top");
 
  520  wmove(help_win, 7, col2);
 
  521  wprintw(help_win, 
"- G: move selection to bottom");
 
  522  wmove(help_win, 8, col2);
 
  523  wprintw(help_win, 
"- /: open search prompt");
 
  524  wmove(help_win, 9, col2);
 
  525  wprintw(help_win, 
"- %%: open regex prompt");
 
  526  wmove(help_win, 10, col2);
 
  527  wprintw(help_win, 
"- w: wipe the search/regex status");
 
  528  wmove(help_win, 11, col2);
 
  529  wprintw(help_win, 
"- m: toggle grouping matches on top");
 
  530  wmove(help_win, 13, col1);
 
  531  wprintw(help_win, 
"Press ESC to dismiss this win");
 
  532  wmove(help_win, 14, col1);
 
  533  wprintw(help_win, 
"Version %s", clbsk::version::v().c_str());
 
  535  wnoutrefresh(help_win);
 
  545   || boost::chrono::duration_cast<boost::chrono::seconds>(channel.last_view())
 
  546    > boost::chrono::seconds(2);
 
 
  549void Screen::write_head()
 const 
  551  bool const problematic = std::find_if(
 
  560  if (m_smatched.empty()) {
 
  563      , 
"CLBSK - %s - # CLBs:%c %ld - 'q' to quit, 'h' for help" 
  565      , problematic ? 
'!' : 
' ' 
  571      , 
"CLBSK - %s - # CLBs:%c %ld (%ld) - 'q' to quit, 'h' for help" 
  573      , problematic ? 
'!' : 
' ' 
  579  wnoutrefresh(m_hwin);
 
  583void Screen::write_page()
 
  588    mvwaddstr(m_cwin, 0, 3, 
"^");
 
  591  if (m_lower < 
static_cast<int>(m_list.size())) {
 
  592    mvwaddstr(m_cwin, LINES - 3, 3, 
"v");
 
  599  typedef ChList::const_iterator citerator;
 
  601  citerator it = m_list.begin(), et = m_list.end();
 
  602  std::advance(it, m_upper);
 
  604  for (
int vcount = m_upper; it != et && vcount < m_lower; ++vcount, ++it) {
 
  605    wmove(m_cwin, ++count, 2);
 
  610      , COLS + m_offset - 3
 
  612      , std::find(m_smatched.begin(), m_smatched.end(), it) != m_smatched.end()
 
  615    if (count - 2 == m_selected - m_upper) {
 
  616      mvwaddstr(m_cwin, count, 1, 
">");
 
  619  wnoutrefresh(m_cwin);
 
  623void Screen::move_limits()
 
  625  if (m_upper > m_selected) {
 
  626    m_upper = m_selected;
 
  627    m_lower = m_upper + LINES - 5;
 
  629  if (m_lower <= m_selected) {
 
  630    m_lower = m_selected + 1;
 
  631    m_upper = m_lower - LINES + 5;
 
  637  ChList::const_iterator it = m_list.begin();
 
  638  std::advance(it, m_selected);
 
  644    wprintw(m_cwin, 
"Press ESC to return back.");
 
  649    print_chan(m_cwin, it, m_linebuffer, COLS - 3, 0, 
false);
 
  651    uint32_t 
const*
const data = 
static_cast<uint32_t const*
>(
 
  652        static_cast<void const*
>(header + 1));
 
  668    for (
int i = 0; i < 31; ++i) {
 
  669      wprintw(m_cwin, 
"CH%02d: %5d", i, ntohl(data[i]));
 
  675        wmove(m_cwin, y + 1, 2);
 
  677        wmove(m_cwin, y, x + 2);
 
  684    char const*
const raw_data = 
static_cast<char const*
>(
 
  685        static_cast<void const*
>(header));
 
  687    SCData 
const*
const scdata = 
static_cast<SCData const*
>(
 
  688        static_cast<void const*
>(
 
  689            raw_data + 
sizeof(*header) + 31 * 
sizeof(uint32_t)));
 
  691    wmove(m_cwin, y + 2, 2);
 
  692    wprintw(m_cwin, 
"Validity: 0x%x", ntohl(scdata->valid));
 
  694    wmove(m_cwin, y + 3, 2);
 
  697        "Yaw: %4.4f  Pitch: %4.4f  Roll: %4.4f deg",
 
  698        ntohl_f(scdata->ahrs.yaw),
 
  699        ntohl_f(scdata->ahrs.pitch),
 
  700        ntohl_f(scdata->ahrs.roll));
 
  702    wmove(m_cwin, y + 4, 2);
 
  705        "Acceleration: %4.4f, %4.4f, %4.4f g",
 
  706        ntohl_f(scdata->ahrs.ax),
 
  707        ntohl_f(scdata->ahrs.ay),
 
  708        ntohl_f(scdata->ahrs.az));
 
  710    wmove(m_cwin, y + 5, 2);
 
  713        "Gyroscope: %4.4f, %4.4f, %4.4f deg/sec",
 
  714        ntohl_f(scdata->ahrs.gx),
 
  715        ntohl_f(scdata->ahrs.gy),
 
  716        ntohl_f(scdata->ahrs.gz));
 
  718    wmove(m_cwin, y + 6, 2);
 
  720    "Compass: %4.4f, %4.4f, %4.4f gauss",
 
  721    ntohl_f(scdata->ahrs.hx),
 
  722    ntohl_f(scdata->ahrs.hy),
 
  723    ntohl_f(scdata->ahrs.hz));
 
  725    wmove(m_cwin, y + 7, 2);
 
  726    wprintw(m_cwin, 
"Temperature: %.2f Celsius", ntohs(scdata->temp) / 100.);
 
  728    wmove(m_cwin, y + 8, 2);
 
  729    wprintw(m_cwin, 
"Humidity: %.2f RH", ntohs(scdata->humidity) / 100.);
 
  731    wnoutrefresh(m_cwin);
 
  736bool matches(mon_channel 
const& ch, std::string 
const& pattern)
 
  741          ch.name().find(pattern)                                    != std::string::npos
 
  742       || ch.mac_address().find(pattern)                             != std::string::npos
 
  743       || boost::lexical_cast<std::string>(ch.domid()).find(pattern) != std::string::npos
 
 
  752      ChList::const_iterator it = m_list.begin(), et = m_list.end()
 
  756    if (
matches(*it, m_search_editor.current())) {
 
  757      m_smatched.push_back(it);
 
  762bool matches(mon_channel 
const& ch, boost::regex 
const& regex)
 
  765      boost::regex_match(ch.name(), regex)
 
  766   || boost::regex_match(ch.mac_address(), regex)
 
  767   || boost::regex_match(boost::lexical_cast<std::string>(ch.domid()), regex);
 
 
  775      ChList::const_iterator it = m_list.begin(), et = m_list.end()
 
  780      m_smatched.push_back(it);
 
  785bool Screen::is_filter()
 const 
  787  return status == kSINGLE;
 
  790bool Screen::is_user_input()
 const 
  792  return status >= kSEARCH;
 
  807  bool operator ()(mon_channel 
const& first, mon_channel 
const& second)
 
  810        ? first.domid() < second.domid()
 
  811        : first.domid() > second.domid();
 
 
 
  827  bool operator ()(mon_channel 
const& first, mon_channel 
const& second)
 
  829    using namespace boost::chrono;
 
  831        ? duration_cast<seconds>(first.last_view()) < duration_cast<seconds>(second.last_view())
 
  832        : duration_cast<seconds>(first.last_view()) > duration_cast<seconds>(second.last_view());
 
 
 
  848  bool operator ()(mon_channel 
const& first, mon_channel 
const& second)
 
  851        ? first.mean() < second.mean()
 
  852        : first.mean() > second.mean();
 
 
 
  868  bool operator ()(mon_channel 
const& first, mon_channel 
const& second)
 
  871        ? first.run_number() < second.run_number()
 
  872        : first.run_number() > second.run_number();
 
 
 
  888  bool operator ()(mon_channel 
const& first, mon_channel 
const& second)
 
  891        ? first.name() < second.name()
 
  892        : first.name() > second.name();
 
 
 
  908      if ((*(*it)).domid() == ch.domid()) {
 
 
  923  bool operator ()(mon_channel 
const& first, mon_channel 
const& second)
 
  925    bool const first_matches = 
belongs(first);
 
  926    bool const second_matches = 
belongs(second);
 
  928    return first_matches || !second_matches;
 
 
 
  932void Screen::sort_channels()
 
  936      m_list.sort(domid_comparator(m_sort_reverse));
 
  939      m_list.sort(last_view_comparator(m_sort_reverse));
 
  942      m_list.sort(hit_rate_comparator(m_sort_reverse));
 
  945      m_list.sort(run_number_comparator(m_sort_reverse));
 
  948      m_list.sort(name_comparator(m_sort_reverse));
 
  951      assert(!
"Sort internal error");
 
  954  if (m_matching_first && !m_smatched.empty() && go_match != NONE) {
 
  955    m_list.sort(match_comparator(m_smatched));
 
  961  static const char head[] =
 
  962      "DOMID      MAC ADDRESS       Name     Run #   Hit rate (min, max, avg)       Last viewed   S Delta time";
 
  964  if (
static_cast<int>(strlen(head)) > offset) {
 
  965    int const max_in_string = strlen(head) - offset;
 
  966    int const nchars =  std::min(COLS - 4, max_in_string);
 
  967    wprintw(win, 
"%.*s", nchars, head + offset);
 
 
  973  , ChList::const_iterator 
const& it
 
  979  assert(offset <= line_size);
 
  981  int const last_view = boost::chrono::duration_cast<boost::chrono::seconds>(
 
  985  int const size = snprintf(
 
  988      "%-9d  %-17s %-8s %-6u  %7.2f, %7.2f, %7.2f kHz  %4d sec ago  %s %ld ms",
 
  990      it->mac_address().c_str(),
 
  997      it->syncd() ? 
"*" : 
" ",
 
  998      it->avg_delta_time());
 
 1000  if (offset < std::min(line_size, size)) {
 
 1002      wattron(win, A_REVERSE);
 
 1004      wattroff(win, A_REVERSE);
 
 1008      wattron(win, NOSYNC);
 
 1012      wattron(win, HIRATE);
 
 1015    if (last_view > 2) {
 
 1016      wattron(win, NODATA);
 
 1019    wprintw(win, 
"%s", &buffer[offset]);
 
 1021    if (size > line_size) {
 
 1026  wattrset(win, GROUND);
 
 
 1037  assert(offset <= line_size);
 
 1039  std::ostringstream oss;
 
 1041  oss << UTCTime_h(tstamp, validity);
 
 1043  int const size = snprintf(
 
 1046      "Sec: %d, Tic: %-8d - %s",
 
 1051  if (offset < std::min(line_size, size)) {
 
 1052    wprintw(win, 
"%s", &buffer[offset]);
 
 1054    if (size > line_size) {