about summary refs log blame commit diff stats
path: root/090trace_browser.cc
blob: d98b7fabfa4654a0f62c188af409b9f0a4193e66 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11


                                                            


                                             
                                                    



                                       





                                                







                                                     
                       
                           
                                
                                   
                                                                  



                            
                                             


                                                                      

                                                                 
                                               

                                                                      
                                                          

                        

                                   
                 

                        






                                                         
                                                 
                                  

                                                        
                                               
                                

                                         
                     
                                     


                      
                                        


                                  
                                        

                                  
                                           
                  




                                                                 
                                           
                                   
                                                                                             

                                      
                                                                          
                          
       
                             

                              





                                                                                             
                                                                          






                                     
                                        
                                                
                                                     
                                                           



                                                                                    
                                                
                                                                   


                                                                                 
                                      



                                                                   


                                

                            
                                                              
                                         
                                                     

                                                           

                                                                                
                                                                                    
                                                    
                                                                   
                                                    


                                                             

                                                               


                            



                

                                                                                     
                                          
                      

                                                                                                                                          
                                           



                                                             







                                                                
                                                      
                                                                                 

                                                                                                   



                                                     
                                  

                                  

                                       
                         
                                  







                                                  
                                                      

                                                




                                                                  

                                                   
                       
                                                           
                                         
                                                                 



                                                        

                                   
                                                             

   








                                                 







                                           








                                                                       
                                                                    
 
//: A debugging helper that lets you zoom in/out on a trace.

//: browse the trace we just created
:(before "End Primitive Recipe Declarations")
_BROWSE_TRACE,
:(before "End Primitive Recipe Numbers")
put(Recipe_ordinal, "$browse-trace", _BROWSE_TRACE);
:(before "End Primitive Recipe Checks")
case _BROWSE_TRACE: {
  break;
}
:(before "End Primitive Recipe Implementations")
case _BROWSE_TRACE: {
  start_trace_browser();
  break;
}

//: browse a trace loaded from a file
:(after "Commandline Parsing")
if (argc == 3 && is_equal(argv[1], "browse-trace")) {
  load_trace(argv[2]);
  start_trace_browser();
  return 0;
}

:(before "End Globals")
set<long long int> Visible;
long long int Top_of_screen = 0;
long long int Last_printed_row = 0;
map<int, long long int> Trace_index;  // screen row -> trace index

:(code)
void start_trace_browser() {
  if (!Trace_stream) return;
  cerr << "computing min depth to display\n";
  long long int min_depth = 9999;
  for (long long int i = 0; i < SIZE(Trace_stream->past_lines); ++i) {
    trace_line& curr_line = Trace_stream->past_lines.at(i);
    if (curr_line.depth < min_depth) min_depth = curr_line.depth;
  }
  cerr << "min depth is " << min_depth << '\n';
  cerr << "computing lines to display\n";
  for (long long int i = 0; i < SIZE(Trace_stream->past_lines); ++i) {
    if (Trace_stream->past_lines.at(i).depth == min_depth)
      Visible.insert(i);
  }
  tb_init();
  Display_row = Display_column = 0;
  tb_event event;
  Top_of_screen = 0;
  refresh_screen_rows();
  while (true) {
    render();
    do {
      tb_poll_event(&event);
    } while (event.type != TB_EVENT_KEY);
    long long int key = event.key ? event.key : event.ch;
    if (key == 'q' || key == 'Q') break;
    if (key == 'j' || key == TB_KEY_ARROW_DOWN) {
      // move cursor one line down
      if (Display_row < Last_printed_row) ++Display_row;
    }
    if (key == 'k' || key == TB_KEY_ARROW_UP) {
      // move cursor one line up
      if (Display_row > 0) --Display_row;
    }
    if (key == 'H') {
      // move cursor to top of screen
      Display_row = 0;
    }
    if (key == 'M') {
      // move cursor to center of screen
      Display_row = tb_height()/2;
    }
    if (key == 'L') {
      // move cursor to bottom of screen
      Display_row = tb_height()-1;
    }
    if (key == 'J' || key == TB_KEY_PGDN) {
      // page-down
      if (Trace_index.find(tb_height()-1) != Trace_index.end()) {
        Top_of_screen = Trace_index[tb_height()-1]+1;
        refresh_screen_rows();
      }
    }
    if (key == 'K' || key == TB_KEY_PGUP) {
      // page-up is more convoluted
      for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) {
        --Top_of_screen;
        if (Top_of_screen <= 0) break;
        while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen))
          --Top_of_screen;
      }
      if (Top_of_screen >= 0)
        refresh_screen_rows();
    }
    if (key == 'G') {
      // go to bottom of screen; largely like page-up, interestingly
      Top_of_screen = SIZE(Trace_stream->past_lines)-1;
      for (int screen_row = tb_height(); screen_row > 0 && Top_of_screen > 0; --screen_row) {
        --Top_of_screen;
        if (Top_of_screen <= 0) break;
        while (Top_of_screen > 0 && !contains_key(Visible, Top_of_screen))
          --Top_of_screen;
      }
      refresh_screen_rows();
      // move cursor to bottom
      Display_row = Last_printed_row;
      refresh_screen_rows();
    }
    if (key == TB_KEY_CARRIAGE_RETURN) {
      // expand lines under current by one level
      assert(contains_key(Trace_index, Display_row));
      long long int start_index = Trace_index[Display_row];
      long long int index = 0;
      // simultaneously compute end_index and min_depth
      int min_depth = 9999;
      for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) {
        if (contains_key(Visible, index)) break;
        trace_line& curr_line = Trace_stream->past_lines.at(index);
        assert(curr_line.depth > Trace_stream->past_lines.at(start_index).depth);
        if (curr_line.depth < min_depth) min_depth = curr_line.depth;
      }
      long long int end_index = index;
      // mark as visible all intervening indices at min_depth
      for (index = start_index; index < end_index; ++index) {
        trace_line& curr_line = Trace_stream->past_lines.at(index);
        if (curr_line.depth == min_depth) {
          Visible.insert(index);
        }
      }
      refresh_screen_rows();
    }
    if (key == TB_KEY_BACKSPACE || key == TB_KEY_BACKSPACE2) {
      // collapse all lines under current
      assert(contains_key(Trace_index, Display_row));
      long long int start_index = Trace_index[Display_row];
      long long int index = 0;
      // end_index is the next line at a depth same as or lower than start_index
      int initial_depth = Trace_stream->past_lines.at(start_index).depth;
      for (index = start_index+1; index < SIZE(Trace_stream->past_lines); ++index) {
        if (!contains_key(Visible, index)) continue;
        trace_line& curr_line = Trace_stream->past_lines.at(index);
        if (curr_line.depth <= initial_depth) break;
      }
      long long int end_index = index;
      // mark as visible all intervening indices at min_depth
      for (index = start_index+1; index < end_index; ++index) {
        Visible.erase(index);
      }
      refresh_screen_rows();
    }
  }
  tb_shutdown();
}

// update Trace_indices for each screen_row on the basis of Top_of_screen and Visible
void refresh_screen_rows() {
  long long int screen_row = 0, index = 0;
  Trace_index.clear();
  for (screen_row = 0, index = Top_of_screen; screen_row < tb_height() && index < SIZE(Trace_stream->past_lines); ++screen_row, ++index) {
    // skip lines without depth for now
    while (!contains_key(Visible, index)) {
      ++index;
      if (index >= SIZE(Trace_stream->past_lines)) goto done;
    }
    assert(index < SIZE(Trace_stream->past_lines));
    Trace_index[screen_row] = index;
  }
done:;
}

void render() {
  long long int screen_row = 0;
  for (screen_row = 0; screen_row < tb_height(); ++screen_row) {
    if (!contains_key(Trace_index, screen_row)) break;
    trace_line& curr_line = Trace_stream->past_lines.at(Trace_index[screen_row]);
    ostringstream out;
    out << std::setw(4) << curr_line.depth << ' ' << curr_line.label << ": " << curr_line.contents;
    if (screen_row < tb_height()-1) {
      long long int delta = lines_hidden(screen_row);
      // home-brew escape sequence for red
      if (delta > 999) out << "{";
      out << " (" << delta << ")";
      if (delta > 999) out << "}";
    }
    render_line(screen_row, out.str());
  }
  // clear rest of screen
  Last_printed_row = screen_row-1;
  for (; screen_row < tb_height(); ++screen_row) {
    render_line(screen_row, "~");
  }
  // move cursor back to display row at the end
  tb_set_cursor(0, Display_row);
  tb_present();
}

long long int lines_hidden(long long int screen_row) {
  assert(contains_key(Trace_index, screen_row));
  if (!contains_key(Trace_index, screen_row+1))
    return SIZE(Trace_stream->past_lines)-Trace_index[screen_row];
  else
    return Trace_index[screen_row+1] - Trace_index[screen_row];
}

void render_line(int screen_row, const string& s) {
  long long int col = 0;
  int color = TB_WHITE;
  for (col = 0; col < tb_width() && col < SIZE(s); ++col) {
    char c = s.at(col);  // todo: unicode
    if (c == '\n') c = ';';  // replace newlines with semi-colons
    // escapes. hack: can't start a line with them.
    if (c == '{') { color = /*red*/1; c = ' '; }
    if (c == '}') { color = TB_WHITE; c = ' '; }
    tb_change_cell(col, screen_row, c, color, TB_BLACK);
  }
  for (; col < tb_width(); ++col) {
    tb_change_cell(col, screen_row, ' ', TB_WHITE, TB_BLACK);
  }
}

void load_trace(const char* filename) {
  ifstream tin(filename);
  if (!tin) {
    cerr << "no such file: " << filename << '\n';
    exit(1);
  }
  Trace_stream = new trace_stream;
  while (has_data(tin)) {
    tin >> std::noskipws;
      skip_whitespace_but_not_newline(tin);
      if (!isdigit(tin.peek())) {
        string dummy;
        getline(tin, dummy);
        continue;
      }
    tin >> std::skipws;
    int depth;
    tin >> depth;
    string label;
    tin >> label;
    if (*--label.end() == ':') label.erase(--label.end());
    string line;
    getline(tin, line);
    Trace_stream->past_lines.push_back(trace_line(depth, label, line));
  }
  cerr << "lines read: " << Trace_stream->past_lines.size() << '\n';
}
<string.h> #include <assert.h> #include "config.h" #include "common.h" #include "config/theme.h" #include "config/preferences.h" #include "ui/ui.h" #include "ui/titlebar.h" #include "ui/inputwin.h" #include "window_list.h" #include "ui/window.h" #include "roster_list.h" #include "chat_session.h" static WINDOW *win; static contact_presence_t current_presence; static gboolean tls_secured; static gboolean is_connected; static gboolean typing; static GTimer *typing_elapsed; static void _title_bar_draw(void); static void _show_self_presence(void); static void _show_contact_presence(ProfChatWin *chatwin); static void _show_privacy(ProfChatWin *chatwin); void create_title_bar(void) { int cols = getmaxx(stdscr); win = newwin(1, cols, 0, 0); wbkgd(win, theme_attrs(THEME_TITLE_TEXT)); title_bar_console(); title_bar_set_presence(CONTACT_OFFLINE); title_bar_set_tls(FALSE); title_bar_set_connected(FALSE); wnoutrefresh(win); inp_put_back(); } void title_bar_update_virtual(void) { ProfWin *window = wins_get_current(); if (window->type != WIN_CONSOLE) { if (typing_elapsed) { gdouble seconds = g_timer_elapsed(typing_elapsed, NULL); if (seconds >= 10) { typing = FALSE; g_timer_destroy(typing_elapsed); typing_elapsed = NULL; } } } _title_bar_draw(); } void title_bar_resize(void) { int cols = getmaxx(stdscr); wresize(win, 1, cols); wbkgd(win, theme_attrs(THEME_TITLE_TEXT)); _title_bar_draw(); } void title_bar_console(void) { werase(win); if (typing_elapsed) { g_timer_destroy(typing_elapsed); } typing_elapsed = NULL; typing = FALSE; _title_bar_draw(); } void title_bar_set_presence(contact_presence_t presence) { current_presence = presence; _title_bar_draw(); } void title_bar_set_connected(gboolean connected) { is_connected = connected; _title_bar_draw(); } void title_bar_set_tls(gboolean secured) { tls_secured = secured; _title_bar_draw(); } void title_bar_switch(void) { if (typing_elapsed) { g_timer_destroy(typing_elapsed); typing_elapsed = NULL; typing = FALSE; } _title_bar_draw(); } void title_bar_set_typing(gboolean is_typing) { if (is_typing) { if (typing_elapsed) { g_timer_start(typing_elapsed); } else { typing_elapsed = g_timer_new(); } } typing = is_typing; _title_bar_draw(); } static void _title_bar_draw(void) { ProfWin *current = wins_get_current(); werase(win); wmove(win, 0, 0); int i; for (i = 0; i < 45; i++) { waddch(win, ' '); } char *title = win_get_title(current); mvwprintw(win, 0, 0, " %s", title); free(title); if (current && current->type == WIN_CHAT) { ProfChatWin *chatwin = (ProfChatWin*) current; assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); _show_contact_presence(chatwin); _show_privacy(chatwin); if (typing) { wprintw(win, " (typing...)"); } } _show_self_presence(); wnoutrefresh(win); inp_put_back(); } static void _show_self_presence(void) { int presence_attrs = 0; int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET); int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED); int unencrypted_attrs = theme_attrs(THEME_TITLE_UNENCRYPTED); int cols = getmaxx(stdscr); int tls_start = 0; switch (current_presence) { case CONTACT_ONLINE: presence_attrs = theme_attrs(THEME_TITLE_ONLINE); wattron(win, bracket_attrs); mvwaddch(win, 0, cols - 9, '['); wattroff(win, bracket_attrs); wattron(win, presence_attrs); mvwprintw(win, 0, cols - 8, "online"); wattroff(win, presence_attrs); tls_start = 15; break; case CONTACT_AWAY: presence_attrs = theme_attrs(THEME_TITLE_AWAY); wattron(win, bracket_attrs); mvwaddch(win, 0, cols - 7, '['); wattroff(win, bracket_attrs); wattron(win, presence_attrs); mvwprintw(win, 0, cols - 6, "away"); wattroff(win, presence_attrs); tls_start = 13; break; case CONTACT_DND: presence_attrs = theme_attrs(THEME_TITLE_DND); wattron(win, bracket_attrs); mvwaddch(win, 0, cols - 6, '['); wattroff(win, bracket_attrs); wattron(win, presence_attrs); mvwprintw(win, 0, cols - 5, "dnd"); wattroff(win, presence_attrs); tls_start = 12; break; case CONTACT_CHAT: presence_attrs = theme_attrs(THEME_TITLE_CHAT); wattron(win, bracket_attrs); mvwaddch(win, 0, cols - 7, '['); wattroff(win, bracket_attrs); wattron(win, presence_attrs); mvwprintw(win, 0, cols - 6, "chat"); wattroff(win, presence_attrs); tls_start = 13; break; case CONTACT_XA: presence_attrs = theme_attrs(THEME_TITLE_XA); wattron(win, bracket_attrs); mvwaddch(win, 0, cols - 5, '['); wattroff(win, bracket_attrs); wattron(win, presence_attrs); mvwprintw(win, 0, cols - 4, "xa"); wattroff(win, presence_attrs); tls_start = 11; break; case CONTACT_OFFLINE: presence_attrs = theme_attrs(THEME_TITLE_OFFLINE); wattron(win, bracket_attrs); mvwaddch(win, 0, cols - 10, '['); wattroff(win, bracket_attrs); wattron(win, presence_attrs); mvwprintw(win, 0, cols - 9, "offline"); wattroff(win, presence_attrs); tls_start = 16; break; } wattron(win, bracket_attrs); mvwaddch(win, 0, cols - 2, ']'); wattroff(win, bracket_attrs); if (is_connected && prefs_get_boolean(PREF_TLS_SHOW)) { wattron(win, bracket_attrs); mvwaddch(win, 0, cols - tls_start, '['); wattroff(win, bracket_attrs); if (tls_secured) { wattron(win, encrypted_attrs); mvwprintw(win, 0, cols - (tls_start - 1), "TLS"); wattroff(win, encrypted_attrs); } else { wattron(win, unencrypted_attrs); mvwprintw(win, 0, cols - (tls_start - 1), "TLS"); wattroff(win, unencrypted_attrs); } wattron(win, bracket_attrs); mvwaddch(win, 0, cols - (tls_start - 4), ']'); wattroff(win, bracket_attrs); } } static void _show_privacy(ProfChatWin *chatwin) { int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET); int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED); int unencrypted_attrs = theme_attrs(THEME_TITLE_UNENCRYPTED); int trusted_attrs = theme_attrs(THEME_TITLE_TRUSTED); int untrusted_attrs = theme_attrs(THEME_TITLE_UNTRUSTED); if (chatwin->is_otr) { wprintw(win, " "); wattron(win, bracket_attrs); wprintw(win, "["); wattroff(win, bracket_attrs); wattron(win, encrypted_attrs); wprintw(win, "OTR"); wattroff(win, encrypted_attrs); wattron(win, bracket_attrs); wprintw(win, "]"); wattroff(win, bracket_attrs); if (chatwin->otr_is_trusted) { wprintw(win, " "); wattron(win, bracket_attrs); wprintw(win, "["); wattroff(win, bracket_attrs); wattron(win, trusted_attrs); wprintw(win, "trusted"); wattroff(win, trusted_attrs); wattron(win, bracket_attrs); wprintw(win, "]"); wattroff(win, bracket_attrs); } else { wprintw(win, " "); wattron(win, bracket_attrs); wprintw(win, "["); wattroff(win, bracket_attrs); wattron(win, untrusted_attrs); wprintw(win, "untrusted"); wattroff(win, untrusted_attrs); wattron(win, bracket_attrs); wprintw(win, "]"); wattroff(win, bracket_attrs); } } else if (chatwin->pgp_send || chatwin->pgp_recv) { GString *pgpmsg = g_string_new("PGP "); if (chatwin->pgp_send && !chatwin->pgp_recv) { g_string_append(pgpmsg, "send"); } else if (!chatwin->pgp_send && chatwin->pgp_recv) { g_string_append(pgpmsg, "recv"); } else { g_string_append(pgpmsg, "send/recv"); } wprintw(win, " "); wattron(win, bracket_attrs); wprintw(win, "["); wattroff(win, bracket_attrs); wattron(win, encrypted_attrs); wprintw(win, pgpmsg->str); wattroff(win, encrypted_attrs); wattron(win, bracket_attrs); wprintw(win, "]"); wattroff(win, bracket_attrs); g_string_free(pgpmsg, TRUE); } else { if (prefs_get_boolean(PREF_ENC_WARN)) { wprintw(win, " "); wattron(win, bracket_attrs); wprintw(win, "["); wattroff(win, bracket_attrs); wattron(win, unencrypted_attrs); wprintw(win, "unencrypted"); wattroff(win, unencrypted_attrs); wattron(win, bracket_attrs); wprintw(win, "]"); wattroff(win, bracket_attrs); } } } static void _show_contact_presence(ProfChatWin *chatwin) { int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET); char *resource = NULL; ChatSession *session = chat_session_get(chatwin->barejid); if (chatwin->resource_override) { resource = chatwin->resource_override; } else if (session && session->resource) { resource = session->resource; } if (resource && prefs_get_boolean(PREF_RESOURCE_TITLE)) { wprintw(win, "/"); wprintw(win, resource); } if (prefs_get_boolean(PREF_PRESENCE)) { theme_item_t presence_colour = THEME_TITLE_OFFLINE; const char *presence = "offline"; PContact contact = roster_get_contact(chatwin->barejid); if (contact) { if (resource) { Resource *resourcep = p_contact_get_resource(contact, resource); if (resourcep) { presence = string_from_resource_presence(resourcep->presence); } } else { presence = p_contact_presence(contact); } } presence_colour = THEME_TITLE_ONLINE; if (g_strcmp0(presence, "offline") == 0) { presence_colour = THEME_TITLE_OFFLINE; } else if (g_strcmp0(presence, "away") == 0) { presence_colour = THEME_TITLE_AWAY; } else if (g_strcmp0(presence, "xa") == 0) { presence_colour = THEME_TITLE_XA; } else if (g_strcmp0(presence, "chat") == 0) { presence_colour = THEME_TITLE_CHAT; } else if (g_strcmp0(presence, "dnd") == 0) { presence_colour = THEME_TITLE_DND; } int presence_attrs = theme_attrs(presence_colour); wprintw(win, " "); wattron(win, bracket_attrs); wprintw(win, "["); wattroff(win, bracket_attrs); wattron(win, presence_attrs); wprintw(win, presence); wattroff(win, presence_attrs); wattron(win, bracket_attrs); wprintw(win, "]"); wattroff(win, bracket_attrs); } }