[spectro] add a summary overlay to the selected position and show log messages in a panel

This commit is contained in:
Timothy Stack 2022-07-05 11:06:37 -07:00
parent cec6c3a225
commit 52fe2db376
44 changed files with 1593 additions and 664 deletions

6
NEWS
View File

@ -18,6 +18,12 @@ lnav v0.10.2:
(activated by the -m option), a log format can be created from
a regular expression entry on regex101.com and existing patterns
can be edited.
* In the spectrogram view, the selected value range is now shown by
an overlay that includes a summary of the range and the number of
values that fall in that range. There is also a detail panel at
the bottom that shows the log-messages/DB-rows whose values are in
that range. You can then press TAB to focus on the detail view
and scroll around.
* Add initial support for pcap(3) files using tshark(1).
* To make it possible to automate some operations, there is now an
"lnav_events" table that is updated when internal events occur

View File

@ -51,6 +51,8 @@ set(TIME_FORMATS
"%a %b %d %H:%M:%S %Z %Y"
"%a %b %d %H:%M:%S "
"%a %b %d %H:%M:%S.%L "
"%a %b %d %H:%M "
"%a %b %e %H:%M:%S %Z %Y"
"%d/%b/%Y:%H:%M:%S +0000"
"%d/%b/%Y:%H:%M:%S %z"
"%d-%b-%Y %H:%M:%S %z"
@ -328,6 +330,8 @@ add_library(
string-extension-functions.cc
sysclip.cc
piper_proc.cc
pollable.cc
spectro_impls.cc
spectro_source.cc
sql_commands.cc
sql_util.cc
@ -415,6 +419,7 @@ add_library(
papertrail_proc.hh
pcap_manager.hh
plain_text_source.hh
pollable.hh
pretty_printer.hh
preview_status_source.hh
pugixml/pugiconfig.hpp
@ -436,6 +441,7 @@ add_library(
shlex.hh
shlex.resolver.hh
simdutf8check.h
spectro_impls.hh
spectro_source.hh
sqlitepp.hh
sql_help.hh

View File

@ -258,6 +258,7 @@ noinst_HEADERS = \
pcap_manager.hh \
piper_proc.hh \
plain_text_source.hh \
pollable.hh \
pretty_printer.hh \
preview_status_source.hh \
ptimec.hh \
@ -281,6 +282,7 @@ noinst_HEADERS = \
shlex.hh \
shlex.resolver.hh \
simdutf8check.h \
spectro_impls.hh \
spectro_source.hh \
sqlitepp.hh \
sql_help.hh \
@ -422,6 +424,7 @@ libdiag_a_SOURCES = \
papertrail_proc.cc \
pcap_manager.cc \
plain_text_source.cc \
pollable.cc \
pretty_printer.cc \
ptimec_rt.cc \
readline_callbacks.cc \
@ -435,6 +438,7 @@ libdiag_a_SOURCES = \
session_data.cc \
shared_buffer.cc \
shlex.cc \
spectro_impls.cc \
spectro_source.cc \
sqlite-extension-func.cc \
statusview_curses.cc \

View File

@ -30,6 +30,7 @@ add_library(
auto_fd.hh
auto_mem.hh
auto_pid.hh
bus.hh
date_time_scanner.hh
enum_util.hh
fs_util.hh

View File

@ -25,6 +25,7 @@ noinst_HEADERS = \
auto_fd.hh \
auto_mem.hh \
auto_pid.hh \
bus.hh \
date_time_scanner.hh \
enum_util.hh \
file_range.hh \

64
src/base/bus.hh Normal file
View File

@ -0,0 +1,64 @@
/**
* Copyright (c) 2022, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef lnav_bus_hh
#define lnav_bus_hh
#include <vector>
#include "lnav_log.hh"
template<typename T>
class bus {
public:
bus() = default;
virtual ~bus() { require(this->b_components.empty()); }
bus(const bus<T>&) = delete;
void attach(T* component)
{
this->b_components.template emplace_back(component);
}
void detach(T* component)
{
auto iter = std::find(
this->b_components.begin(), this->b_components.end(), component);
require(iter != this->b_components.end());
this->b_components.erase(iter);
}
protected:
std::vector<T*> b_components;
};
#endif

View File

@ -65,11 +65,28 @@ create_factory() noexcept
template<typename T, typename... Annotations>
struct bind : singleton_storage<T, Annotations...> {
template<typename I = T,
std::enable_if_t<has_injectable<I>::value, bool> = true>
static bool to_singleton() noexcept
{
static T storage;
typename I::injectable* i = nullptr;
singleton_storage<T, Annotations...>::ss_owner
= create_from_injectable<I>(i)();
singleton_storage<T, Annotations...>::ss_data
= singleton_storage<T, Annotations...>::ss_owner.get();
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
singleton_storage<T, Annotations...>::ss_data = &storage;
return true;
}
template<typename I = T,
std::enable_if_t<!has_injectable<I>::value, bool> = true>
static bool to_singleton() noexcept
{
singleton_storage<T, Annotations...>::ss_owner = std::make_shared<T>();
singleton_storage<T, Annotations...>::ss_data
= singleton_storage<T, Annotations...>::ss_owner.get();
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
return true;
}
@ -78,12 +95,14 @@ struct bind : singleton_storage<T, Annotations...> {
{
singleton_storage<T, Annotations...>::ss_data
= f(::injector::get<Args>()...);
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
return true;
}
static bool to_instance(T* data) noexcept
{
singleton_storage<T, Annotations...>::ss_data = data;
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
return true;
}
@ -92,6 +111,7 @@ struct bind : singleton_storage<T, Annotations...> {
{
singleton_storage<T, Annotations...>::ss_factory
= details::create_factory<I>();
singleton_storage<T, Annotations...>::ss_scope = scope::none;
return true;
}
};

View File

@ -43,6 +43,12 @@
namespace injector {
enum class scope {
undefined,
none,
singleton,
};
template<typename Annotation>
void force_linking(Annotation anno);
@ -63,10 +69,16 @@ struct singleton_storage {
{
static int _[] = {0, (force_linking(Annotations{}), 0)...};
(void) _;
assert(ss_data != nullptr);
return ss_data;
}
static std::shared_ptr<T> get_owner()
{
static int _[] = {0, (force_linking(Annotations{}), 0)...};
(void) _;
return ss_owner;
}
static std::shared_ptr<T> create()
{
static int _[] = {0, (force_linking(Annotations{}), 0)...};
@ -75,13 +87,21 @@ struct singleton_storage {
}
protected:
static scope ss_scope;
static T* ss_data;
static std::shared_ptr<T> ss_owner;
static std::function<std::shared_ptr<T>()> ss_factory;
};
template<typename T, typename... Annotations>
T* singleton_storage<T, Annotations...>::ss_data = nullptr;
template<typename T, typename... Annotations>
scope singleton_storage<T, Annotations...>::ss_scope = scope::undefined;
template<typename T, typename... Annotations>
std::shared_ptr<T> singleton_storage<T, Annotations...>::ss_owner;
template<typename T, typename... Annotations>
std::function<std::shared_ptr<T>()>
singleton_storage<T, Annotations...>::ss_factory;
@ -153,14 +173,33 @@ template<class T>
struct is_vector<std::vector<T>> : std::true_type {
};
template<typename I, typename R, typename... IArgs, typename... Args>
std::function<std::shared_ptr<I>()> create_from_injectable(R (*)(IArgs...),
Args&... args);
template<typename T,
typename... Args,
std::enable_if_t<has_injectable<typename T::element_type>::value,
bool> = true,
std::enable_if_t<is_shared_ptr<T>::value, bool> = true>
T
get(Args&... args)
{
typename T::element_type::injectable* i = nullptr;
return create_from_injectable<typename T::element_type>(i, args...)();
}
template<typename T,
typename... Annotations,
std::enable_if_t<!has_injectable<typename T::element_type>::value,
bool> = true,
std::enable_if_t<is_shared_ptr<T>::value, bool> = true>
T
get()
{
return singleton_storage<typename T::element_type,
Annotations...>::create();
Annotations...>::get_owner();
}
template<typename T, std::enable_if_t<is_vector<T>::value, bool> = true>
@ -170,6 +209,15 @@ get()
return multiple_storage<typename T::value_type::element_type>::create();
}
template<typename I, typename R, typename... IArgs, typename... Args>
std::function<std::shared_ptr<I>()>
create_from_injectable(R (*)(IArgs...), Args&... args)
{
return [&]() {
return std::make_shared<I>(args..., ::injector::get<IArgs>()...);
};
}
} // namespace injector
#endif

View File

@ -115,7 +115,13 @@ const char* lnav_log_crash_dir;
nonstd::optional<const struct termios*> lnav_log_orig_termios;
// NOTE: This mutex is leaked so that it is not destroyed during exit.
// Otherwise, any attempts to log will fail.
static std::mutex* lnav_log_mutex = new std::mutex();
static std::mutex*
lnav_log_mutex()
{
static auto* retval = new std::mutex();
return retval;
}
static std::vector<log_state_dumper*>&
DUMPER_LIST()
@ -306,7 +312,7 @@ log_msg(lnav_log_level_t level,
return;
}
std::lock_guard<std::mutex> log_lock(*lnav_log_mutex);
std::lock_guard<std::mutex> log_lock(*lnav_log_mutex());
{
// get the base name of the file. NB: can't use basename() since it
@ -365,7 +371,7 @@ log_msg(lnav_log_level_t level,
void
log_msg_extra(const char* fmt, ...)
{
std::lock_guard<std::mutex> mg(*lnav_log_mutex);
std::lock_guard<std::mutex> mg(*lnav_log_mutex());
va_list args;
va_start(args, fmt);
@ -382,7 +388,7 @@ log_msg_extra(const char* fmt, ...)
void
log_msg_extra_complete()
{
std::lock_guard<std::mutex> mg(*lnav_log_mutex);
std::lock_guard<std::mutex> mg(*lnav_log_mutex());
auto line = log_alloc();
line[0] = '\n';
log_ring.lr_length += 1;

View File

@ -63,4 +63,11 @@ roundup_size(size_t size, int step)
return retval;
}
template<typename T>
T
abs_diff(T a, T b)
{
return a > b ? a - b : b - a;
}
#endif

View File

@ -33,6 +33,7 @@
#include "base/opt_util.hh"
#include "config.h"
#include "files_sub_source.hh"
#include "filter_sub_source.hh"
#include "lnav.hh"
static auto TOGGLE_MSG = "Press " ANSI_BOLD("TAB") " to edit ";
@ -246,11 +247,11 @@ filter_help_status_source::statusview_fields()
}
if (lnav_data.ld_mode == ln_mode_t::FILTER) {
auto& editor = lnav_data.ld_filter_source;
static auto* editor = injector::get<filter_sub_source*>();
auto& lv = lnav_data.ld_filter_view;
auto& fs = tss->get_filters();
if (editor.fss_editing) {
if (editor->fss_editing) {
auto tf = *(fs.begin() + lv.get_selection());
auto lang = tf->get_lang() == filter_lang_t::SQL ? "an SQL"
: "a regular";

View File

@ -37,23 +37,25 @@
#include "readline_highlighters.hh"
#include "readline_possibilities.hh"
filter_sub_source::filter_sub_source()
filter_sub_source::filter_sub_source(std::shared_ptr<readline_curses> editor)
: fss_editor(editor)
{
this->fss_regex_context.set_highlighter(readline_regex_highlighter)
.set_append_character(0);
this->fss_editor.add_context(filter_lang_t::REGEX, this->fss_regex_context);
this->fss_editor->add_context(filter_lang_t::REGEX,
this->fss_regex_context);
this->fss_sql_context.set_highlighter(readline_sqlite_highlighter)
.set_append_character(0);
this->fss_editor.add_context(filter_lang_t::SQL, this->fss_sql_context);
this->fss_editor.set_change_action(
this->fss_editor->add_context(filter_lang_t::SQL, this->fss_sql_context);
this->fss_editor->set_change_action(
bind_mem(&filter_sub_source::rl_change, this));
this->fss_editor.set_perform_action(
this->fss_editor->set_perform_action(
bind_mem(&filter_sub_source::rl_perform, this));
this->fss_editor.set_abort_action(
this->fss_editor->set_abort_action(
bind_mem(&filter_sub_source::rl_abort, this));
this->fss_editor.set_display_match_action(
this->fss_editor->set_display_match_action(
bind_mem(&filter_sub_source::rl_display_matches, this));
this->fss_editor.set_display_next_action(
this->fss_editor->set_display_next_action(
bind_mem(&filter_sub_source::rl_display_next, this));
this->fss_match_view.set_sub_source(&this->fss_match_source);
this->fss_match_view.set_height(0_vl);
@ -67,10 +69,10 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
if (this->fss_editing) {
switch (ch) {
case KEY_CTRL_RBRACKET:
this->fss_editor.abort();
this->fss_editor->abort();
return true;
default:
this->fss_editor.handle_key(ch);
this->fss_editor->handle_key(ch);
return true;
}
}
@ -156,26 +158,26 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
this->fss_editing = true;
add_view_text_possibilities(&this->fss_editor,
add_view_text_possibilities(this->fss_editor.get(),
filter_lang_t::REGEX,
"*",
top_view,
text_quoting::regex);
this->fss_editor.set_window(lv.get_window());
this->fss_editor.set_visible(true);
this->fss_editor.set_y(lv.get_y()
+ (int) (lv.get_selection() - lv.get_top()));
this->fss_editor.set_left(25);
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
this->fss_editor.focus(filter_lang_t::REGEX, "", "");
this->fss_editor->set_window(lv.get_window());
this->fss_editor->set_visible(true);
this->fss_editor->set_y(
lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
this->fss_editor->set_left(25);
this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1);
this->fss_editor->focus(filter_lang_t::REGEX, "", "");
this->fss_filter_state = true;
ef->disable();
return true;
}
case 'o': {
textview_curses* top_view = *lnav_data.ld_view_stack.top();
text_sub_source* tss = top_view->get_sub_source();
filter_stack& fs = tss->get_filters();
auto* top_view = *lnav_data.ld_view_stack.top();
auto* tss = top_view->get_sub_source();
auto& fs = tss->get_filters();
auto filter_index = fs.next_index();
if (!filter_index) {
@ -192,18 +194,18 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
this->fss_editing = true;
add_view_text_possibilities(&this->fss_editor,
add_view_text_possibilities(this->fss_editor.get(),
filter_lang_t::REGEX,
"*",
top_view,
text_quoting::regex);
this->fss_editor.set_window(lv.get_window());
this->fss_editor.set_visible(true);
this->fss_editor.set_y(lv.get_y()
+ (int) (lv.get_selection() - lv.get_top()));
this->fss_editor.set_left(25);
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
this->fss_editor.focus(filter_lang_t::REGEX, "", "");
this->fss_editor->set_window(lv.get_window());
this->fss_editor->set_visible(true);
this->fss_editor->set_y(
lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
this->fss_editor->set_left(25);
this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1);
this->fss_editor->focus(filter_lang_t::REGEX, "", "");
this->fss_filter_state = true;
ef->disable();
return true;
@ -226,17 +228,17 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
? text_quoting::sql
: text_quoting::regex;
add_view_text_possibilities(
&this->fss_editor, tf->get_lang(), "*", top_view, tq);
this->fss_editor.get(), tf->get_lang(), "*", top_view, tq);
add_filter_expr_possibilities(
&this->fss_editor, filter_lang_t::SQL, "*");
this->fss_editor.set_window(lv.get_window());
this->fss_editor.set_visible(true);
this->fss_editor.set_y(lv.get_y()
+ (int) (lv.get_selection() - lv.get_top()));
this->fss_editor.set_left(25);
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
this->fss_editor.focus(tf->get_lang(), "");
this->fss_editor.rewrite_line(0, tf->get_id().c_str());
this->fss_editor.get(), filter_lang_t::SQL, "*");
this->fss_editor->set_window(lv.get_window());
this->fss_editor->set_visible(true);
this->fss_editor->set_y(
lv.get_y() + (int) (lv.get_selection() - lv.get_top()));
this->fss_editor->set_left(25);
this->fss_editor->set_width(this->tss_view->get_width() - 8 - 1);
this->fss_editor->focus(tf->get_lang(), "");
this->fss_editor->rewrite_line(0, tf->get_id().c_str());
this->fss_filter_state = tf->is_enabled();
tf->disable();
tss->text_filters_changed();
@ -565,7 +567,7 @@ filter_sub_source::rl_perform(readline_curses* rc)
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
lnav_data.ld_filter_help_status_source.fss_prompt.clear();
this->fss_editing = false;
this->fss_editor.set_visible(false);
this->fss_editor->set_visible(false);
this->tss_view->reload_data();
}
@ -585,7 +587,7 @@ filter_sub_source::rl_abort(readline_curses* rc)
top_view->reload_data();
fs.delete_filter("");
this->tss_view->reload_data();
this->fss_editor.set_visible(false);
this->fss_editor->set_visible(false);
this->fss_editing = false;
this->tss_view->set_needs_update();
tf->set_enabled(this->fss_filter_state);

View File

@ -30,6 +30,7 @@
#ifndef filter_sub_source_hh
#define filter_sub_source_hh
#include "base/injector.hh"
#include "plain_text_source.hh"
#include "readline_curses.hh"
#include "textview_curses.hh"
@ -38,7 +39,12 @@ class filter_sub_source
: public text_sub_source
, public list_input_delegate {
public:
filter_sub_source();
filter_sub_source(std::shared_ptr<readline_curses> editor);
using injectable
= filter_sub_source(std::shared_ptr<readline_curses> editor);
filter_sub_source(const filter_sub_source*) = delete;
~filter_sub_source() override = default;
@ -75,7 +81,7 @@ public:
readline_context fss_regex_context{"filter-regex", nullptr, false};
readline_context fss_sql_context{"filter-sql", nullptr, false};
readline_curses fss_editor;
std::shared_ptr<readline_curses> fss_editor;
plain_text_source fss_match_source;
textview_curses fss_match_view;

View File

@ -47,8 +47,11 @@
#include "vis_line.hh"
template<typename LineType>
grep_proc<LineType>::grep_proc(pcre* code, grep_proc_source<LineType>& gps)
: gp_pcre(code), gp_source(gps)
grep_proc<LineType>::grep_proc(pcre* code,
grep_proc_source<LineType>& gps,
std::shared_ptr<pollable_supervisor> ps)
: pollable(ps, pollable::category::background), gp_pcre(code),
gp_source(gps)
{
require(this->invariant());
@ -88,7 +91,9 @@ grep_proc<LineType>::start()
{
require(this->invariant());
log_debug("grep_proc(%p): start", this);
if (this->gp_child_started || this->gp_queue.empty()) {
log_debug("grep_proc(%p): nothing to do?", this);
return;
}
@ -126,6 +131,8 @@ grep_proc<LineType>::start()
this->gp_child_queue_size = this->gp_queue.size();
this->gp_queue.clear();
log_debug("grep_proc(%p): started child %d", this, this->gp_child);
return;
}
@ -402,4 +409,17 @@ grep_proc<LineType>::invalidate()
return *this;
}
template<typename LineType>
void
grep_proc<LineType>::update_poll_set(std::vector<struct pollfd>& pollfds)
{
if (this->gp_line_buffer.get_fd() != -1) {
pollfds.push_back(
(struct pollfd){this->gp_line_buffer.get_fd(), POLLIN, 0});
}
if (this->gp_err_pipe != -1) {
pollfds.push_back((struct pollfd){this->gp_err_pipe, POLLIN, 0});
}
}
template class grep_proc<vis_line_t>;

View File

@ -47,6 +47,7 @@
#include "base/lnav_log.hh"
#include "line_buffer.hh"
#include "pcrepp/pcrepp.hh"
#include "pollable.hh"
#include "strong_int.hh"
template<typename LineType>
@ -159,7 +160,7 @@ public:
* library directly.
*/
template<typename LineType>
class grep_proc {
class grep_proc : public pollable {
public:
class error : public std::exception {
public:
@ -175,16 +176,22 @@ public:
* @param code The pcre code to run over the lines of input.
* @param gps The source of the data to match.
*/
grep_proc(pcre* code, grep_proc_source<LineType>& gps);
grep_proc(pcre* code,
grep_proc_source<LineType>& gps,
std::shared_ptr<pollable_supervisor> ps);
grep_proc(std::shared_ptr<pollable_supervisor>);
using injectable = grep_proc(std::shared_ptr<pollable_supervisor>);
virtual ~grep_proc();
/** @param gpd The sink to send resuls to. */
/** @param gpd The sink to send results to. */
void set_sink(grep_proc_sink<LineType>* gpd) { this->gp_sink = gpd; }
grep_proc& invalidate();
/** @param gpd The sink to send results to. */
/** @param gpc The control to send results to. */
void set_control(grep_proc_control* gpc) { this->gp_control = gpc; }
/** @return The sink to send results to. */
@ -216,23 +223,14 @@ public:
*/
void start();
void update_poll_set(std::vector<struct pollfd>& pollfds)
{
if (this->gp_line_buffer.get_fd() != -1) {
pollfds.push_back(
(struct pollfd){this->gp_line_buffer.get_fd(), POLLIN, 0});
}
if (this->gp_err_pipe != -1) {
pollfds.push_back((struct pollfd){this->gp_err_pipe, POLLIN, 0});
}
}
void update_poll_set(std::vector<struct pollfd>& pollfds) override;
/**
* Check the fd_set to see if there is any new data to be processed.
*
* @param ready_rfds The set of ready-to-read file descriptors.
*/
void check_poll_set(const std::vector<struct pollfd>& pollfds);
void check_poll_set(const std::vector<struct pollfd>& pollfds) override;
/** Check the invariants for this object. */
bool invariant()

View File

@ -828,6 +828,8 @@ handle_paging_key(int ch)
});
tc->reload_data();
} else if (tc == &lnav_data.ld_views[LNV_SPECTRO]) {
lnav_data.ld_mode = ln_mode_t::SPECTRO_DETAILS;
} else if (tc_tss != nullptr && tc_tss->tss_supports_filtering) {
lnav_data.ld_mode = lnav_data.ld_last_config_mode;
lnav_data.ld_filter_view.reload_data();

View File

@ -54,10 +54,17 @@ listview_curses::reload_data()
this->lv_top
= std::max(0_vl, vis_line_t(this->get_inner_height() - 1));
}
if (this->get_inner_height() == 0) {
this->lv_selection = 0_vl;
} else if (this->lv_selection >= this->get_inner_height()) {
this->lv_selection = this->get_inner_height() - 1_vl;
if (this->lv_selectable) {
if (this->get_inner_height() == 0) {
this->lv_selection = 0_vl;
} else if (this->lv_selection >= this->get_inner_height()) {
this->set_selection(this->get_inner_height() - 1_vl);
} else {
auto curr_sel = this->lv_selection;
this->lv_selection = -1_vl;
this->set_selection(curr_sel);
}
}
}
this->vc_needs_update = true;
@ -482,8 +489,8 @@ listview_curses::set_top(vis_line_t top, bool suppress_flash)
auto bot = this->get_bottom();
if (bot != -1_vl) {
if (this->lv_selection > bot) {
this->lv_selection = bot;
if (this->lv_selection > (bot - this->lv_tail_space)) {
this->lv_selection = bot - this->lv_tail_space;
}
}
}
@ -559,9 +566,48 @@ listview_curses::scroll_selection_into_view()
}
if (this->lv_selection < 0_vl) {
this->set_top(0_vl);
} else if (this->lv_selection >= (this->lv_top + height - 1)) {
this->set_top(this->lv_selection - height + 2_vl, true);
} else if (this->lv_selection
>= (this->lv_top + height - this->lv_tail_space)) {
this->set_top(this->lv_selection - height + 1_vl + this->lv_tail_space,
true);
} else if (this->lv_selection < this->lv_top) {
this->set_top(this->lv_selection, true);
}
}
void
listview_curses::set_selection(vis_line_t sel)
{
if (this->lv_selectable) {
auto inner_height = this->get_inner_height();
if (this->lv_selection != sel && sel >= 0_vl && sel < inner_height) {
auto found = false;
vis_line_t step;
if (sel == 0_vl) {
step = 1_vl;
} else if (sel == inner_height - 1_vl) {
step = -1_vl;
} else if (sel < this->lv_selection) {
step = -1_vl;
} else {
step = 1_vl;
}
while (sel < inner_height) {
if (this->lv_source->listview_is_row_selectable(*this, sel)) {
found = true;
break;
}
sel += step;
}
if (found) {
this->lv_selection = sel;
this->scroll_selection_into_view();
this->lv_source->listview_selection_changed(*this);
this->set_needs_update();
}
}
} else {
this->set_top(sel);
}
}

View File

@ -76,6 +76,14 @@ public:
{
return "";
}
virtual bool listview_is_row_selectable(const listview_curses& lv,
vis_line_t row)
{
return true;
}
virtual void listview_selection_changed(const listview_curses& lv) {}
};
class list_gutter_source {
@ -194,14 +202,7 @@ public:
bool is_selectable() const { return this->lv_selectable; }
void set_selection(vis_line_t sel)
{
if (this->lv_selection != sel) {
this->lv_selection = sel;
this->scroll_selection_into_view();
this->set_needs_update();
}
}
void set_selection(vis_line_t sel);
void scroll_selection_into_view();
@ -209,7 +210,10 @@ public:
vis_line_t get_selection() const
{
return this->lv_selection;
if (this->lv_selectable) {
return this->lv_selection;
}
return this->lv_top;
}
listview_curses& set_word_wrap(bool ww)
@ -553,4 +557,5 @@ protected:
lv_mode_t lv_mouse_mode{lv_mode_t::NONE};
vis_line_t lv_tail_space{1};
};
#endif

View File

@ -93,6 +93,7 @@
#include "CLI/CLI.hpp"
#include "dump_internals.hh"
#include "environ_vtab.hh"
#include "filter_sub_source.hh"
#include "fstat_vtab.hh"
#include "grep_proc.hh"
#include "help-txt.h"
@ -119,6 +120,7 @@
#include "regexp_vtab.hh"
#include "service_tags.hh"
#include "session_data.hh"
#include "spectro_source.hh"
#include "sql_help.hh"
#include "sql_util.hh"
#include "sqlite-extension-func.hh"
@ -167,8 +169,6 @@ using namespace lnav::roles::literals;
static std::vector<std::string> DEFAULT_FILES;
static auto intern_lifetime = intern_string::get_table_lifetime();
struct lnav_data_t lnav_data;
const int ZOOM_LEVELS[] = {
1,
30,
@ -217,6 +217,12 @@ static const std::vector<std::string> DEFAULT_DB_KEY_NAMES = {
const static size_t MAX_STDIN_CAPTURE_SIZE = 10 * 1024 * 1024;
static auto bound_pollable_supervisor
= injector::bind<pollable_supervisor>::to_singleton();
static auto bound_filter_sub_source
= injector::bind<filter_sub_source>::to_singleton();
static auto bound_active_files = injector::bind<file_collection>::to_instance(
+[]() { return &lnav_data.ld_active_files; });
@ -278,6 +284,8 @@ force_linking(services::main_t anno)
static breadcrumb_curses breadcrumb_view;
struct lnav_data_t lnav_data;
bool
setup_logline_table(exec_context& ec)
{
@ -848,10 +856,39 @@ handle_key(int ch)
case ln_mode_t::FILES:
return handle_config_ui_key(ch);
case ln_mode_t::SPECTRO_DETAILS: {
if (ch == '\t' || ch == 'q') {
lnav_data.ld_mode = ln_mode_t::PAGING;
return true;
}
if (lnav_data.ld_spectro_details_view.handle_key(ch)) {
return true;
}
switch (ch) {
case 'n': {
execute_command(lnav_data.ld_exec_context,
"next-mark search");
return true;
}
case 'N': {
execute_command(lnav_data.ld_exec_context,
"prev-mark search");
return true;
}
case '/': {
execute_command(lnav_data.ld_exec_context,
"prompt search-spectro-details");
return true;
}
}
return false;
}
case ln_mode_t::COMMAND:
case ln_mode_t::SEARCH:
case ln_mode_t::SEARCH_FILTERS:
case ln_mode_t::SEARCH_FILES:
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
case ln_mode_t::CAPTURE:
case ln_mode_t::SQL:
case ln_mode_t::EXEC:
@ -946,6 +983,9 @@ wait_for_pipers()
static void
looper()
{
static auto* ps = injector::get<pollable_supervisor*>();
static auto* filter_source = injector::get<filter_sub_source*>();
try {
auto_fd errpipe[2];
auto_fd::pipe(errpipe);
@ -964,11 +1004,13 @@ looper()
readline_context search_filters_context(
"search-filters", nullptr, false);
readline_context search_files_context("search-files", nullptr, false);
readline_context search_spectro_details_context(
"search-spectro-details", nullptr, false);
readline_context index_context("capture");
readline_context sql_context("sql", sql_cmd_map, false);
readline_context exec_context("exec");
readline_context user_context("user");
readline_curses rlc;
auto rlc = injector::get<std::shared_ptr<readline_curses>>();
sig_atomic_t overlay_counter = 0;
int lpc;
@ -979,6 +1021,8 @@ looper()
readline_regex_highlighter);
search_files_context.set_append_character(0).set_highlighter(
readline_regex_highlighter);
search_spectro_details_context.set_append_character(0).set_highlighter(
readline_regex_highlighter);
sql_context.set_highlighter(readline_sqlite_highlighter)
.set_quote_chars("\"")
.with_readline_var((char**) &rl_completer_word_break_characters,
@ -994,19 +1038,21 @@ looper()
auto& sb = lnav_data.ld_scroll_broadcaster;
auto& vsb = lnav_data.ld_view_stack_broadcaster;
rlc.add_context(ln_mode_t::COMMAND, command_context);
rlc.add_context(ln_mode_t::SEARCH, search_context);
rlc.add_context(ln_mode_t::SEARCH_FILTERS, search_filters_context);
rlc.add_context(ln_mode_t::SEARCH_FILES, search_files_context);
rlc.add_context(ln_mode_t::CAPTURE, index_context);
rlc.add_context(ln_mode_t::SQL, sql_context);
rlc.add_context(ln_mode_t::EXEC, exec_context);
rlc.add_context(ln_mode_t::USER, user_context);
rlc.start();
rlc->add_context(ln_mode_t::COMMAND, command_context);
rlc->add_context(ln_mode_t::SEARCH, search_context);
rlc->add_context(ln_mode_t::SEARCH_FILTERS, search_filters_context);
rlc->add_context(ln_mode_t::SEARCH_FILES, search_files_context);
rlc->add_context(ln_mode_t::SEARCH_SPECTRO_DETAILS,
search_spectro_details_context);
rlc->add_context(ln_mode_t::CAPTURE, index_context);
rlc->add_context(ln_mode_t::SQL, sql_context);
rlc->add_context(ln_mode_t::EXEC, exec_context);
rlc->add_context(ln_mode_t::USER, user_context);
rlc->start();
lnav_data.ld_filter_source.fss_editor.start();
filter_source->fss_editor->start();
lnav_data.ld_rl_view = &rlc;
lnav_data.ld_rl_view = rlc.get();
lnav_data.ld_rl_view->add_possibility(
ln_mode_t::COMMAND, "viewname", lnav_view_strings);
@ -1088,19 +1134,19 @@ looper()
execute_examples();
rlc.set_window(lnav_data.ld_window);
rlc.set_y(-1);
rlc.set_focus_action(rl_focus);
rlc.set_change_action(rl_change);
rlc.set_perform_action(rl_callback);
rlc.set_alt_perform_action(rl_alt_callback);
rlc.set_timeout_action(rl_search);
rlc.set_abort_action(lnav_rl_abort);
rlc.set_display_match_action(rl_display_matches);
rlc.set_display_next_action(rl_display_next);
rlc.set_blur_action(rl_blur);
rlc.set_completion_request_action(rl_completion_request);
rlc.set_alt_value(HELP_MSG_2(
rlc->set_window(lnav_data.ld_window);
rlc->set_y(-1);
rlc->set_focus_action(rl_focus);
rlc->set_change_action(rl_change);
rlc->set_perform_action(rl_callback);
rlc->set_alt_perform_action(rl_alt_callback);
rlc->set_timeout_action(rl_search);
rlc->set_abort_action(lnav_rl_abort);
rlc->set_display_match_action(rl_display_matches);
rlc->set_display_next_action(rl_display_next);
rlc->set_blur_action(rl_blur);
rlc->set_completion_request_action(rl_completion_request);
rlc->set_alt_value(HELP_MSG_2(
e, E, "to move forward/backward through error messages"));
(void) curs_set(0);
@ -1127,20 +1173,21 @@ looper()
breadcrumb_view.set_y(0);
breadcrumb_view.set_window(lnav_data.ld_window);
breadcrumb_view.set_line_source(lnav_crumb_source);
auto event_handler = [](auto&& tc) {
auto top_view = lnav_data.ld_view_stack.top();
if (top_view && *top_view == &tc) {
lnav_data.ld_bottom_source.update_search_term(tc);
}
};
for (lpc = 0; lpc < LNV__MAX; lpc++) {
lnav_data.ld_views[lpc].set_window(lnav_data.ld_window);
lnav_data.ld_views[lpc].set_y(1);
lnav_data.ld_views[lpc].set_height(
vis_line_t(-(rlc.get_height() + 1)));
vis_line_t(-(rlc->get_height() + 1)));
lnav_data.ld_views[lpc].set_scroll_action(sb);
lnav_data.ld_views[lpc].set_search_action(update_hits);
lnav_data.ld_views[lpc].tc_state_event_handler = [](auto&& tc) {
auto top_view = lnav_data.ld_view_stack.top();
if (top_view && *top_view == &tc) {
lnav_data.ld_bottom_source.update_search_term(tc);
}
};
lnav_data.ld_views[lpc].tc_state_event_handler = event_handler;
}
lnav_data.ld_doc_view.set_window(lnav_data.ld_window);
@ -1167,7 +1214,25 @@ looper()
lnav_data.ld_user_message_view.set_window(lnav_data.ld_window);
lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc.get_height() + 1));
lnav_data.ld_spectro_details_view.set_window(lnav_data.ld_window);
lnav_data.ld_spectro_details_view.set_show_scrollbar(true);
lnav_data.ld_spectro_details_view.set_height(5_vl);
lnav_data.ld_spectro_details_view.set_sub_source(
&lnav_data.ld_spectro_no_details_source);
lnav_data.ld_spectro_details_view.tc_state_event_handler
= event_handler;
lnav_data.ld_spectro_details_view.set_scroll_action(sb);
lnav_data.ld_spectro_no_details_source.replace_with(
attr_line_t().append(
lnav::roles::comment(" No details available")));
lnav_data.ld_spectro_source->ss_details_view
= &lnav_data.ld_spectro_details_view;
lnav_data.ld_spectro_source->ss_no_details_source
= &lnav_data.ld_spectro_no_details_source;
lnav_data.ld_spectro_source->ss_exec_context
= &lnav_data.ld_exec_context;
lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc->get_height() + 1));
for (auto& sc : lnav_data.ld_status) {
sc.set_window(lnav_data.ld_window);
}
@ -1181,6 +1246,10 @@ looper()
&lnav_data.ld_doc_status_source);
lnav_data.ld_status[LNS_PREVIEW].set_data_source(
&lnav_data.ld_preview_status_source);
lnav_data.ld_spectro_status_source
= std::make_unique<spectro_status_source>();
lnav_data.ld_status[LNS_SPECTRO].set_data_source(
lnav_data.ld_spectro_status_source.get());
lnav_data.ld_match_view.set_show_bottom_border(true);
lnav_data.ld_user_message_view.set_show_bottom_border(true);
@ -1372,6 +1441,7 @@ looper()
lnav_data.ld_example_view.do_update();
lnav_data.ld_match_view.do_update();
lnav_data.ld_preview_view.do_update();
lnav_data.ld_spectro_details_view.do_update();
lnav_data.ld_user_message_view.do_update();
if (ui_clock::now() >= next_status_update_time) {
for (auto& sc : lnav_data.ld_status) {
@ -1379,8 +1449,8 @@ looper()
}
next_status_update_time = ui_clock::now() + 100ms;
}
if (lnav_data.ld_filter_source.fss_editing) {
lnav_data.ld_filter_source.fss_match_view.set_needs_update();
if (filter_source->fss_editing) {
filter_source->fss_match_view.set_needs_update();
}
breadcrumb_view.do_update();
// These updates need to be done last so their readline views can
@ -1402,7 +1472,7 @@ looper()
if (lnav_data.ld_mode != ln_mode_t::FILTER
&& lnav_data.ld_mode != ln_mode_t::FILES)
{
rlc.do_update();
rlc->do_update();
}
refresh();
@ -1415,10 +1485,11 @@ looper()
case ln_mode_t::SEARCH:
case ln_mode_t::SEARCH_FILTERS:
case ln_mode_t::SEARCH_FILES:
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
case ln_mode_t::SQL:
case ln_mode_t::EXEC:
case ln_mode_t::USER:
if (rlc.consume_ready_for_input()) {
if (rlc->consume_ready_for_input()) {
// log_debug("waiting for readline input")
view_curses::awaiting_user_input();
}
@ -1430,15 +1501,8 @@ looper()
}
}
}
rlc.update_poll_set(pollfds);
lnav_data.ld_filter_source.fss_editor.update_poll_set(pollfds);
for (auto& tc : lnav_data.ld_views) {
tc.update_poll_set(pollfds);
}
lnav_data.ld_filter_view.update_poll_set(pollfds);
lnav_data.ld_files_view.update_poll_set(pollfds);
ps->update_poll_set(pollfds);
ui_now = ui_clock::now();
auto poll_to
= (!changes && ui_now < loop_deadline && session_stage >= 1)
@ -1505,6 +1569,7 @@ looper()
case ln_mode_t::PAGING:
case ln_mode_t::FILTER:
case ln_mode_t::FILES:
case ln_mode_t::SPECTRO_DETAILS:
if (old_gen
== lnav_data.ld_active_files
.fc_files_generation) {
@ -1518,6 +1583,7 @@ looper()
case ln_mode_t::SEARCH:
case ln_mode_t::SEARCH_FILTERS:
case ln_mode_t::SEARCH_FILES:
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
case ln_mode_t::CAPTURE:
case ln_mode_t::SQL:
case ln_mode_t::EXEC:
@ -1528,20 +1594,13 @@ looper()
next_rebuild_time = next_rescan_time;
}
for (auto& tc : lnav_data.ld_views) {
tc.check_poll_set(pollfds);
}
ps->check_poll_set(pollfds);
lnav_data.ld_view_stack.top() |
[](auto tc) { lnav_data.ld_bottom_source.update_hits(tc); };
auto old_mode = lnav_data.ld_mode;
auto old_file_names_size
= lnav_data.ld_active_files.fc_file_names.size();
rlc.check_poll_set(pollfds);
lnav_data.ld_filter_source.fss_editor.check_poll_set(pollfds);
lnav_data.ld_filter_view.check_poll_set(pollfds);
lnav_data.ld_files_view.check_poll_set(pollfds);
if (lnav_data.ld_mode != old_mode) {
switch (lnav_data.ld_mode) {
@ -1692,9 +1751,9 @@ looper()
if (ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0) {
resizeterm(size.ws_row, size.ws_col);
}
rlc.do_update();
rlc.window_change();
lnav_data.ld_filter_source.fss_editor.window_change();
rlc->do_update();
rlc->window_change();
filter_source->fss_editor->window_change();
for (auto& sc : lnav_data.ld_status) {
sc.window_change();
}
@ -1704,6 +1763,7 @@ looper()
lnav_data.ld_match_view.set_needs_update();
lnav_data.ld_filter_view.set_needs_update();
lnav_data.ld_files_view.set_needs_update();
lnav_data.ld_spectro_details_view.set_needs_update();
lnav_data.ld_user_message_view.set_needs_update();
}
@ -1728,10 +1788,6 @@ looper()
gather_pipers();
}
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->start();
}
if (lnav_data.ld_view_stack.empty()
|| (lnav_data.ld_view_stack.size() == 1
&& starting_view_stack_size == 2
@ -1751,22 +1807,15 @@ wait_for_children()
{
std::vector<struct pollfd> pollfds;
struct timeval to = {0, 333000};
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->start();
}
static auto* ps = injector::get<pollable_supervisor*>();
do {
pollfds.clear();
for (auto& tc : lnav_data.ld_views) {
tc.update_poll_set(pollfds);
}
lnav_data.ld_filter_view.update_poll_set(pollfds);
lnav_data.ld_files_view.update_poll_set(pollfds);
auto update_res = ps->update_poll_set(pollfds);
if (pollfds.empty()) {
return;
if (update_res.ur_background == 0) {
break;
}
int rc = poll(&pollfds[0], pollfds.size(), to.tv_usec / 1000);
@ -1781,15 +1830,10 @@ wait_for_children()
}
}
for (auto& tc : lnav_data.ld_views) {
tc.check_poll_set(pollfds);
lnav_data.ld_view_stack.top() |
[](auto tc) { lnav_data.ld_bottom_source.update_hits(tc); };
}
lnav_data.ld_filter_view.check_poll_set(pollfds);
lnav_data.ld_files_view.check_poll_set(pollfds);
} while (true);
ps->check_poll_set(pollfds);
lnav_data.ld_view_stack.top() |
[](auto tc) { lnav_data.ld_bottom_source.update_hits(tc); };
} while (lnav_data.ld_looping);
}
static int
@ -2293,6 +2337,7 @@ main(int argc, char* argv[])
"/usr/share/terminfo:/lib/terminfo:/usr/share/lib/terminfo",
0);
auto* filter_source = injector::get<filter_sub_source*>();
lnav_data.ld_vtab_manager = std::make_unique<log_vtab_manager>(
lnav_data.ld_db, lnav_data.ld_views[LNV_LOG], lnav_data.ld_log_source);
@ -2326,20 +2371,22 @@ main(int argc, char* argv[])
lnav_data.ld_views[LNV_DB].set_sub_source(&lnav_data.ld_db_row_source);
lnav_data.ld_db_overlay.dos_labels = &lnav_data.ld_db_row_source;
lnav_data.ld_views[LNV_DB].set_overlay_source(&lnav_data.ld_db_overlay);
lnav_data.ld_spectro_source = std::make_unique<spectrogram_source>();
lnav_data.ld_views[LNV_SPECTRO]
.set_sub_source(&lnav_data.ld_spectro_source)
.set_overlay_source(&lnav_data.ld_spectro_source)
.add_input_delegate(lnav_data.ld_spectro_source)
.set_tail_space(2_vl);
.set_sub_source(lnav_data.ld_spectro_source.get())
.set_overlay_source(lnav_data.ld_spectro_source.get())
.add_input_delegate(*lnav_data.ld_spectro_source)
.set_tail_space(4_vl);
lnav_data.ld_views[LNV_SPECTRO].set_selectable(true);
lnav_data.ld_doc_view.set_sub_source(&lnav_data.ld_doc_source);
lnav_data.ld_example_view.set_sub_source(&lnav_data.ld_example_source);
lnav_data.ld_match_view.set_sub_source(&lnav_data.ld_match_source);
lnav_data.ld_preview_view.set_sub_source(&lnav_data.ld_preview_source);
lnav_data.ld_filter_view.set_sub_source(&lnav_data.ld_filter_source)
.add_input_delegate(lnav_data.ld_filter_source)
.add_child_view(&lnav_data.ld_filter_source.fss_match_view)
.add_child_view(&lnav_data.ld_filter_source.fss_editor);
lnav_data.ld_filter_view.set_sub_source(filter_source)
.add_input_delegate(*filter_source)
.add_child_view(&filter_source->fss_match_view)
.add_child_view(filter_source->fss_editor.get());
lnav_data.ld_files_view.set_sub_source(&lnav_data.ld_files_source)
.add_input_delegate(lnav_data.ld_files_source);
lnav_data.ld_user_message_view.set_sub_source(

View File

@ -54,7 +54,6 @@
#include "file_collection.hh"
#include "files_sub_source.hh"
#include "filter_status_source.hh"
#include "filter_sub_source.hh"
#include "grep_highlighter.hh"
#include "hist_source.hh"
#include "input_dispatcher.hh"
@ -68,12 +67,14 @@
#include "readline_curses.hh"
#include "relative_time.hh"
#include "safe/safe.h"
#include "spectro_source.hh"
#include "sql_util.hh"
#include "statusview_curses.hh"
#include "textfile_sub_source.hh"
#include "view_helpers.hh"
class spectrogram_source;
class spectro_status_source;
enum {
LNB_HEADLESS,
LNB_MANAGEMENT,
@ -96,6 +97,7 @@ typedef enum {
LNS_FILTER_HELP,
LNS_DOC,
LNS_PREVIEW,
LNS_SPECTRO,
LNS__MAX
} lnav_status_t;
@ -198,6 +200,7 @@ struct lnav_data_t {
filter_help_status_source ld_filter_help_status_source;
doc_status_source ld_doc_status_source;
preview_status_source ld_preview_status_source;
std::unique_ptr<spectro_status_source> ld_spectro_status_source;
bool ld_preview_hidden;
int64_t ld_preview_generation{0};
action_broadcaster<listview_curses> ld_scroll_broadcaster;
@ -207,7 +210,6 @@ struct lnav_data_t {
plain_text_source ld_doc_source;
textview_curses ld_doc_view;
filter_sub_source ld_filter_source;
textview_curses ld_filter_view;
files_sub_source ld_files_source;
files_overlay_source ld_files_overlay;
@ -222,18 +224,19 @@ struct lnav_data_t {
textview_curses ld_user_message_view;
std::chrono::time_point<std::chrono::steady_clock>
ld_user_message_expiration;
textview_curses ld_spectro_details_view;
plain_text_source ld_spectro_no_details_source;
view_stack<textview_curses> ld_view_stack;
textview_curses* ld_last_view;
textview_curses ld_views[LNV__MAX];
std::shared_ptr<grep_proc<vis_line_t>> ld_meta_search;
vis_line_t ld_search_start_line;
readline_curses* ld_rl_view;
logfile_sub_source ld_log_source;
hist_source2 ld_hist_source2;
int ld_zoom_level;
spectrogram_source ld_spectro_source;
std::unique_ptr<spectrogram_source> ld_spectro_source;
textfile_sub_source ld_text_source;

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2007-2012, Timothy Stack
* Copyright (c) 2007-2022, Timothy Stack
*
* All rights reserved.
*
@ -73,6 +73,7 @@
#include "service_tags.hh"
#include "session_data.hh"
#include "shlex.hh"
#include "spectro_impls.hh"
#include "sqlite-extension-func.hh"
#include "sysclip.hh"
#include "tailer/tailer.looper.hh"
@ -221,7 +222,7 @@ com_adjust_log_time(exec_context& ec,
} else if (lnav_data.ld_views[LNV_LOG].get_inner_height() == 0) {
return ec.make_error("no log messages");
} else if (args.size() >= 2) {
logfile_sub_source& lss = lnav_data.ld_log_source;
auto& lss = lnav_data.ld_log_source;
struct timeval top_time, time_diff;
struct timeval new_time = {0, 0};
content_line_t top_content;
@ -230,7 +231,7 @@ com_adjust_log_time(exec_context& ec,
struct exttm tm;
std::shared_ptr<logfile> lf;
top_line = lnav_data.ld_views[LNV_LOG].get_top();
top_line = lnav_data.ld_views[LNV_LOG].get_selection();
top_content = lss.at(top_line);
lf = lss.find(top_content);
@ -375,7 +376,7 @@ com_goto(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
} else if (args.size() > 1) {
std::string all_args = remaining_args(cmdline, args);
auto* tc = *lnav_data.ld_view_stack.top();
auto ttt = dynamic_cast<text_time_translator*>(tc->get_sub_source());
auto* ttt = dynamic_cast<text_time_translator*>(tc->get_sub_source());
int line_number, consumed;
date_time_scanner dts;
const char* scan_end = nullptr;
@ -508,7 +509,7 @@ com_goto(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
} else {
tc->get_sub_source()->get_location_history() |
[new_top](auto lh) { lh->loc_history_append(new_top); };
tc->set_top(new_top);
tc->set_selection(new_top);
retval = "";
}
@ -564,7 +565,7 @@ com_mark(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
if (args.empty() || lnav_data.ld_view_stack.empty()) {
} else if (!ec.ec_dry_run) {
textview_curses* tc = *lnav_data.ld_view_stack.top();
auto* tc = *lnav_data.ld_view_stack.top();
lnav_data.ld_last_user_mark[tc] = tc->get_top();
tc->toggle_user_mark(&textview_curses::BM_USER,
vis_line_t(lnav_data.ld_last_user_mark[tc]));
@ -761,7 +762,7 @@ com_goto_mark(exec_context& ec,
[new_top](auto lh) {
lh->loc_history_append(new_top.value());
};
tc->set_top(new_top.value());
tc->set_selection(new_top.value());
}
lnav_data.ld_bottom_source.grep_error("");
}
@ -783,10 +784,10 @@ com_goto_location(exec_context& ec,
tc->get_sub_source()->get_location_history() |
[tc, &args](auto lh) {
return args[0] == "prev-location"
? lh->loc_history_back(tc->get_top())
: lh->loc_history_forward(tc->get_top());
? lh->loc_history_back(tc->get_selection())
: lh->loc_history_forward(tc->get_selection());
}
| [tc](auto new_top) { tc->set_top(new_top); };
| [tc](auto new_top) { tc->set_selection(new_top); };
};
}
@ -2900,15 +2901,14 @@ com_comment(exec_context& ec,
return ec.make_error(
"The :comment command only works in the log view");
}
logfile_sub_source& lss = lnav_data.ld_log_source;
std::map<content_line_t, bookmark_metadata>& bm
= lss.get_user_bookmark_metadata();
auto& lss = lnav_data.ld_log_source;
auto& bm = lss.get_user_bookmark_metadata();
args[1] = trim(remaining_args(cmdline, args));
tc->set_user_mark(&textview_curses::BM_META, tc->get_top(), true);
bookmark_metadata& line_meta = bm[lss.at(tc->get_top())];
auto& line_meta = bm[lss.at(tc->get_top())];
line_meta.bm_comment = args[1];
lss.set_line_meta_changed();
@ -3207,10 +3207,8 @@ com_clear_partition(exec_context& ec,
} else if (args.size() == 1) {
textview_curses& tc = lnav_data.ld_views[LNV_LOG];
logfile_sub_source& lss = lnav_data.ld_log_source;
bookmark_vector<vis_line_t>& bv
= tc.get_bookmarks()[&textview_curses::BM_META];
std::map<content_line_t, bookmark_metadata>& bm
= lss.get_user_bookmark_metadata();
auto& bv = tc.get_bookmarks()[&textview_curses::BM_META];
auto& bm = lss.get_user_bookmark_metadata();
nonstd::optional<vis_line_t> part_start;
if (binary_search(bv.begin(), bv.end(), tc.get_top())) {
@ -3621,12 +3619,12 @@ com_zoom_to(exec_context& ec,
for (int lpc = 0; lpc < lnav_zoom_strings.size() && !found; lpc++) {
if (strcasecmp(args[1].c_str(), lnav_zoom_strings[lpc].c_str())
== 0) {
spectrogram_source& ss = lnav_data.ld_spectro_source;
auto& ss = *lnav_data.ld_spectro_source;
struct timeval old_time;
lnav_data.ld_zoom_level = lpc;
textview_curses& hist_view = lnav_data.ld_views[LNV_HISTOGRAM];
auto& hist_view = lnav_data.ld_views[LNV_HISTOGRAM];
if (hist_view.get_inner_height() > 0) {
auto old_time_opt = lnav_data.ld_hist_source2.time_for_row(
@ -3642,19 +3640,20 @@ com_zoom_to(exec_context& ec,
}
}
textview_curses& spectro_view = lnav_data.ld_views[LNV_SPECTRO];
auto& spectro_view = lnav_data.ld_views[LNV_SPECTRO];
if (spectro_view.get_inner_height() > 0) {
auto old_time_opt
= lnav_data.ld_spectro_source.time_for_row(
lnav_data.ld_views[LNV_SPECTRO].get_top());
= lnav_data.ld_spectro_source->time_for_row(
lnav_data.ld_views[LNV_SPECTRO].get_selection());
ss.ss_granularity = ZOOM_LEVELS[lnav_data.ld_zoom_level];
ss.invalidate();
spectro_view.reload_data();
if (old_time_opt) {
lnav_data.ld_spectro_source.row_for_time(
lnav_data.ld_spectro_source->row_for_time(
old_time_opt.value())
| [](auto new_top) {
lnav_data.ld_views[LNV_SPECTRO].set_top(
lnav_data.ld_views[LNV_SPECTRO].set_selection(
new_top);
};
}
@ -4399,306 +4398,6 @@ com_reset_config(exec_context& ec,
return Ok(retval);
}
class log_spectro_value_source : public spectrogram_value_source {
public:
log_spectro_value_source(intern_string_t colname)
: lsvs_colname(colname), lsvs_begin_time(0), lsvs_end_time(0),
lsvs_found(false)
{
this->update_stats();
};
void update_stats()
{
logfile_sub_source& lss = lnav_data.ld_log_source;
logfile_sub_source::iterator iter;
this->lsvs_begin_time = 0;
this->lsvs_end_time = 0;
this->lsvs_stats.clear();
for (iter = lss.begin(); iter != lss.end(); iter++) {
std::shared_ptr<logfile> lf = (*iter)->get_file();
if (lf == NULL) {
continue;
}
auto format = lf->get_format();
const auto* stats = format->stats_for_value(this->lsvs_colname);
if (stats == NULL) {
continue;
}
auto ll = lf->begin();
if (this->lsvs_begin_time == 0
|| ll->get_time() < this->lsvs_begin_time) {
this->lsvs_begin_time = ll->get_time();
}
ll = lf->end();
--ll;
if (ll->get_time() > this->lsvs_end_time) {
this->lsvs_end_time = ll->get_time();
}
this->lsvs_found = true;
this->lsvs_stats.merge(*stats);
}
if (this->lsvs_begin_time) {
time_t filtered_begin_time
= lss.find_line(lss.at(0_vl))->get_time();
time_t filtered_end_time
= lss.find_line(lss.at(vis_line_t(lss.text_line_count() - 1)))
->get_time();
if (filtered_begin_time > this->lsvs_begin_time) {
this->lsvs_begin_time = filtered_begin_time;
}
if (filtered_end_time < this->lsvs_end_time) {
this->lsvs_end_time = filtered_end_time;
}
}
};
void spectro_bounds(spectrogram_bounds& sb_out)
{
logfile_sub_source& lss = lnav_data.ld_log_source;
if (lss.text_line_count() == 0) {
return;
}
this->update_stats();
sb_out.sb_begin_time = this->lsvs_begin_time;
sb_out.sb_end_time = this->lsvs_end_time;
sb_out.sb_min_value_out = this->lsvs_stats.lvs_min_value;
sb_out.sb_max_value_out = this->lsvs_stats.lvs_max_value;
sb_out.sb_count = this->lsvs_stats.lvs_count;
};
void spectro_row(spectrogram_request& sr, spectrogram_row& row_out)
{
logfile_sub_source& lss = lnav_data.ld_log_source;
vis_line_t begin_line
= lss.find_from_time(sr.sr_begin_time).value_or(0_vl);
vis_line_t end_line = lss.find_from_time(sr.sr_end_time)
.value_or(lss.text_line_count());
std::vector<logline_value> values;
string_attrs_t sa;
for (vis_line_t curr_line = begin_line; curr_line < end_line;
++curr_line) {
content_line_t cl = lss.at(curr_line);
std::shared_ptr<logfile> lf = lss.find(cl);
auto ll = lf->begin() + cl;
auto format = lf->get_format();
shared_buffer_ref sbr;
if (!ll->is_message()) {
continue;
}
lf->read_full_message(ll, sbr);
sa.clear();
values.clear();
format->annotate(cl, sbr, sa, values, false);
std::vector<logline_value>::iterator lv_iter;
lv_iter = find_if(values.begin(),
values.end(),
logline_value_cmp(&this->lsvs_colname));
if (lv_iter != values.end()) {
switch (lv_iter->lv_meta.lvm_kind) {
case value_kind_t::VALUE_FLOAT:
row_out.add_value(
sr, lv_iter->lv_value.d, ll->is_marked());
break;
case value_kind_t::VALUE_INTEGER:
row_out.add_value(
sr, lv_iter->lv_value.i, ll->is_marked());
break;
default:
break;
}
}
}
};
void spectro_mark(textview_curses& tc,
time_t begin_time,
time_t end_time,
double range_min,
double range_max)
{
// XXX need to refactor this and the above method
textview_curses& log_tc = lnav_data.ld_views[LNV_LOG];
logfile_sub_source& lss = lnav_data.ld_log_source;
vis_line_t begin_line = lss.find_from_time(begin_time).value_or(0_vl);
vis_line_t end_line
= lss.find_from_time(end_time).value_or(lss.text_line_count());
std::vector<logline_value> values;
string_attrs_t sa;
for (vis_line_t curr_line = begin_line; curr_line < end_line;
++curr_line) {
content_line_t cl = lss.at(curr_line);
std::shared_ptr<logfile> lf = lss.find(cl);
auto ll = lf->begin() + cl;
auto format = lf->get_format();
shared_buffer_ref sbr;
if (!ll->is_message()) {
continue;
}
lf->read_full_message(ll, sbr);
sa.clear();
values.clear();
format->annotate(cl, sbr, sa, values, false);
std::vector<logline_value>::iterator lv_iter;
lv_iter = find_if(values.begin(),
values.end(),
logline_value_cmp(&this->lsvs_colname));
if (lv_iter != values.end()) {
switch (lv_iter->lv_meta.lvm_kind) {
case value_kind_t::VALUE_FLOAT:
if (range_min <= lv_iter->lv_value.d
&& lv_iter->lv_value.d <= range_max) {
log_tc.toggle_user_mark(&textview_curses::BM_USER,
curr_line);
}
break;
case value_kind_t::VALUE_INTEGER:
if (range_min <= lv_iter->lv_value.i
&& lv_iter->lv_value.i <= range_max) {
log_tc.toggle_user_mark(&textview_curses::BM_USER,
curr_line);
}
break;
default:
break;
}
}
}
};
intern_string_t lsvs_colname;
logline_value_stats lsvs_stats;
time_t lsvs_begin_time;
time_t lsvs_end_time;
bool lsvs_found;
};
class db_spectro_value_source : public spectrogram_value_source {
public:
db_spectro_value_source(std::string colname)
: dsvs_colname(std::move(colname))
{
this->update_stats();
}
void update_stats()
{
this->dsvs_begin_time = 0;
this->dsvs_end_time = 0;
this->dsvs_stats.clear();
db_label_source& dls = lnav_data.ld_db_row_source;
stacked_bar_chart<std::string>& chart = dls.dls_chart;
date_time_scanner dts;
this->dsvs_column_index = dls.column_name_to_index(this->dsvs_colname);
if (!dls.has_log_time_column()) {
this->dsvs_error_msg
= "no 'log_time' column found or not in ascending order, "
"unable to create spectrogram";
return;
}
if (!this->dsvs_column_index) {
this->dsvs_error_msg = "unknown column -- " + this->dsvs_colname;
return;
}
if (!dls.dls_headers[this->dsvs_column_index.value()].hm_graphable) {
this->dsvs_error_msg
= "column is not numeric -- " + this->dsvs_colname;
return;
}
if (dls.dls_rows.empty()) {
this->dsvs_error_msg = "empty result set";
return;
}
stacked_bar_chart<std::string>::bucket_stats_t bs
= chart.get_stats_for(this->dsvs_colname);
this->dsvs_begin_time = dls.dls_time_column.front().tv_sec;
this->dsvs_end_time = dls.dls_time_column.back().tv_sec;
this->dsvs_stats.lvs_min_value = bs.bs_min_value;
this->dsvs_stats.lvs_max_value = bs.bs_max_value;
this->dsvs_stats.lvs_count = dls.dls_rows.size();
}
void spectro_bounds(spectrogram_bounds& sb_out) override
{
db_label_source& dls = lnav_data.ld_db_row_source;
if (dls.text_line_count() == 0) {
return;
}
this->update_stats();
sb_out.sb_begin_time = this->dsvs_begin_time;
sb_out.sb_end_time = this->dsvs_end_time;
sb_out.sb_min_value_out = this->dsvs_stats.lvs_min_value;
sb_out.sb_max_value_out = this->dsvs_stats.lvs_max_value;
sb_out.sb_count = this->dsvs_stats.lvs_count;
}
void spectro_row(spectrogram_request& sr, spectrogram_row& row_out) override
{
db_label_source& dls = lnav_data.ld_db_row_source;
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
auto end_row = dls.row_for_time({sr.sr_end_time, 0})
.value_or(dls.dls_rows.size());
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
double value = 0.0;
sscanf(dls.dls_rows[lpc][this->dsvs_column_index.value()],
"%lf",
&value);
row_out.add_value(sr, value, false);
}
}
void spectro_mark(textview_curses& tc,
time_t begin_time,
time_t end_time,
double range_min,
double range_max) override{};
std::string dsvs_colname;
logline_value_stats dsvs_stats;
time_t dsvs_begin_time{0};
time_t dsvs_end_time{0};
nonstd::optional<size_t> dsvs_column_index;
std::string dsvs_error_msg;
};
static Result<std::string, lnav::console::user_message>
com_spectrogram(exec_context& ec,
std::string cmdline,
@ -4712,13 +4411,13 @@ com_spectrogram(exec_context& ec,
retval = "";
} else if (args.size() == 2) {
std::string colname = remaining_args(cmdline, args);
spectrogram_source& ss = lnav_data.ld_spectro_source;
auto& ss = *lnav_data.ld_spectro_source;
bool found = false;
ss.ss_granularity = ZOOM_LEVELS[lnav_data.ld_zoom_level];
if (ss.ss_value_source != NULL) {
if (ss.ss_value_source != nullptr) {
delete ss.ss_value_source;
ss.ss_value_source = NULL;
ss.ss_value_source = nullptr;
}
ss.invalidate();
@ -4728,10 +4427,9 @@ com_spectrogram(exec_context& ec,
if (!dsvs->dsvs_error_msg.empty()) {
return ec.make_error("{}", dsvs->dsvs_error_msg);
} else {
ss.ss_value_source = dsvs.release();
found = true;
}
ss.ss_value_source = dsvs.release();
found = true;
} else {
std::unique_ptr<log_spectro_value_source> lsvs(
new log_spectro_value_source(intern_string::lookup(colname)));
@ -4739,16 +4437,16 @@ com_spectrogram(exec_context& ec,
if (!lsvs->lsvs_found) {
return ec.make_error("unknown numeric message field -- {}",
colname);
} else {
ss.ss_value_source = lsvs.release();
found = true;
}
ss.ss_value_source = lsvs.release();
found = true;
}
if (found) {
ss.text_selection_changed(lnav_data.ld_views[LNV_SPECTRO]);
ensure_view(&lnav_data.ld_views[LNV_SPECTRO]);
if (lnav_data.ld_rl_view != NULL) {
if (lnav_data.ld_rl_view != nullptr) {
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_2(z, Z, "to zoom in/out"));
}
@ -4978,6 +4676,24 @@ search_files_prompt(std::vector<std::string>& args)
ANSI_BOLD("CTRL+]") " to abort)");
}
static void
search_spectro_details_prompt(std::vector<std::string>& args)
{
lnav_data.ld_mode = ln_mode_t::SEARCH_SPECTRO_DETAILS;
add_view_text_possibilities(lnav_data.ld_rl_view,
ln_mode_t::SEARCH_SPECTRO_DETAILS,
"*",
&lnav_data.ld_spectro_details_view,
text_quoting::regex);
lnav_data.ld_rl_view->focus(ln_mode_t::SEARCH_SPECTRO_DETAILS,
cget(args, 2).value_or("/"),
cget(args, 3).value_or(""));
lnav_data.ld_bottom_source.set_prompt(
"Search for: "
"(Press " ANSI_BOLD("CTRL+J") " to jump to a previous hit and "
ANSI_BOLD("CTRL+]") " to abort)");
}
static void
sql_prompt(std::vector<std::string>& args)
{
@ -5032,6 +4748,7 @@ com_prompt(exec_context& ec,
{"search", search_prompt},
{"search-filters", search_filters_prompt},
{"search-files", search_files_prompt},
{"search-spectro-details", search_spectro_details_prompt},
{"sql", sql_prompt},
{"user", user_prompt},
};

View File

@ -735,6 +735,22 @@ public:
return retval;
}
bool hide_field(const intern_string_t field_name, bool val) override
{
auto fd_iter
= std::find_if(this->blf_field_defs.begin(),
this->blf_field_defs.end(),
[field_name](const field_def& elem) {
return elem.fd_meta.lvm_name == field_name;
});
if (fd_iter == this->blf_field_defs.end()) {
return false;
}
fd_iter->fd_meta.lvm_user_hidden = val;
return true;
}
std::shared_ptr<log_format> specialized(int fmt_lock = -1) override
{
auto retval = std::make_shared<bro_log_format>(*this);
@ -1337,6 +1353,22 @@ public:
return retval;
}
bool hide_field(const intern_string_t field_name, bool val) override
{
auto fd_iter
= std::find_if(this->wlf_field_defs.begin(),
this->wlf_field_defs.end(),
[field_name](const field_def& elem) {
return elem.fd_meta.lvm_name == field_name;
});
if (fd_iter == this->wlf_field_defs.end()) {
return false;
}
fd_iter->fd_meta.lvm_user_hidden = val;
return true;
}
std::shared_ptr<log_format> specialized(int fmt_lock = -1) override
{
auto retval = std::make_shared<w3c_log_format>(*this);

View File

@ -1340,7 +1340,9 @@ vt_filter(sqlite3_vtab_cursor* p_vtc,
if (vl_max_opt) {
p_cur->log_cursor.lc_end_line = vl_max_opt.value();
for (const auto& msg_info :
vt->lss->window_at(vl_max_opt.value())) {
vt->lss->window_at(vl_max_opt.value(),
vis_line_t(vt->lss->text_line_count())))
{
if (log_time_range->tr_end.value()
< msg_info.get_logline().get_timeval()) {
break;

View File

@ -1963,7 +1963,7 @@ logline_window::begin()
logline_window::iterator
logline_window::end()
{
return {this->lw_source, vis_line_t(this->lw_source.text_line_count())};
return {this->lw_source, this->lw_end_line};
}
logline_window::logmsg_info::logmsg_info(logfile_sub_source& lss, vis_line_t vl)

View File

@ -156,8 +156,10 @@ private:
class logline_window {
public:
logline_window(logfile_sub_source& lss, vis_line_t start_line)
: lw_source(lss), lw_start_line(start_line)
logline_window(logfile_sub_source& lss,
vis_line_t start_vl,
vis_line_t end_vl)
: lw_source(lss), lw_start_line(start_vl), lw_end_line(end_vl)
{
}
@ -177,6 +179,12 @@ public:
return this->li_string_attrs;
}
const std::vector<logline_value>& get_values() const
{
this->load_msg();
return this->li_line_values;
}
std::string to_string(const struct line_range& lr) const;
private:
@ -218,6 +226,7 @@ public:
private:
logfile_sub_source& lw_source;
vis_line_t lw_start_line;
vis_line_t lw_end_line;
};
/**
@ -615,9 +624,9 @@ public:
return this->at(vl);
}
logline_window window_at(vis_line_t vl)
logline_window window_at(vis_line_t start_vl, vis_line_t end_vl)
{
return logline_window(*this, vl);
return logline_window(*this, start_vl, end_vl);
}
log_accel::direction_t get_line_accel_direction(vis_line_t vl);

103
src/pollable.cc Normal file
View File

@ -0,0 +1,103 @@
/**
* Copyright (c) 2022, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "pollable.hh"
#include "base/lnav_log.hh"
pollable::pollable(std::shared_ptr<pollable_supervisor> supervisor,
category cat)
: p_supervisor(supervisor), p_category(cat)
{
log_debug("pollable attach %p to %p", this, this->p_supervisor.get());
this->p_supervisor->attach(this);
}
pollable::~pollable()
{
log_debug("pollable detach %p from %p", this, this->p_supervisor.get());
this->p_supervisor->detach(this);
}
pollable_supervisor::update_result
pollable_supervisor::update_poll_set(std::vector<struct pollfd>& pollfds)
{
update_result retval;
size_t old_size = pollfds.size();
for (auto& pol : this->b_components) {
pol->update_poll_set(pollfds);
switch (pol->get_category()) {
case pollable::category::background:
retval.ur_background += pollfds.size() - old_size;
break;
case pollable::category::interactive:
retval.ur_interactive += pollfds.size() - old_size;
break;
}
old_size = pollfds.size();
}
return retval;
}
void
pollable_supervisor::check_poll_set(const std::vector<struct pollfd>& pollfds)
{
std::vector<pollable*> visited;
auto found_new = false;
// TODO move this loop into the superclass
do {
found_new = false;
for (auto* pol : this->b_components) {
if (std::find(visited.begin(), visited.end(), pol) == visited.end())
{
visited.emplace_back(pol);
pol->check_poll_set(pollfds);
found_new = true;
break;
}
}
} while (found_new);
}
size_t
pollable_supervisor::count(pollable::category cat)
{
size_t retval = 0;
for (const auto* pol : this->b_components) {
if (pol->get_category() == cat) {
retval += 1;
}
}
return retval;
}

79
src/pollable.hh Normal file
View File

@ -0,0 +1,79 @@
/**
* Copyright (c) 2022, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef lnav_pollable_hh
#define lnav_pollable_hh
#include <vector>
#include <poll.h>
#include "base/bus.hh"
class pollable_supervisor;
class pollable {
public:
enum class category {
background,
interactive,
};
pollable(std::shared_ptr<pollable_supervisor> supervisor, category cat);
pollable(const pollable&) = delete;
virtual ~pollable();
category get_category() const { return this->p_category; }
virtual void update_poll_set(std::vector<struct pollfd>& pollfds) = 0;
virtual void check_poll_set(const std::vector<struct pollfd>& pollfds) = 0;
private:
std::shared_ptr<pollable_supervisor> p_supervisor;
const category p_category;
};
class pollable_supervisor : public bus<pollable> {
public:
struct update_result {
size_t ur_background{0};
size_t ur_interactive{0};
};
update_result update_poll_set(std::vector<struct pollfd>& pollfds);
void check_poll_set(const std::vector<struct pollfd>& pollfds);
size_t count(pollable::category cat);
};
#endif

View File

@ -410,6 +410,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
case ln_mode_t::SEARCH:
case ln_mode_t::SEARCH_FILTERS:
case ln_mode_t::SEARCH_FILES:
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
name = "$search";
break;
@ -492,6 +493,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
case ln_mode_t::FILES:
case ln_mode_t::EXEC:
case ln_mode_t::USER:
case ln_mode_t::SPECTRO_DETAILS:
return;
}
@ -573,6 +575,9 @@ rl_callback_int(readline_curses* rc, bool is_alt)
case ln_mode_t::SEARCH_FILES:
new_mode = ln_mode_t::FILES;
break;
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
new_mode = ln_mode_t::SPECTRO_DETAILS;
break;
default:
break;
}
@ -583,6 +588,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
case ln_mode_t::PAGING:
case ln_mode_t::FILTER:
case ln_mode_t::FILES:
case ln_mode_t::SPECTRO_DETAILS:
require(0);
break;
@ -616,6 +622,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
case ln_mode_t::SEARCH:
case ln_mode_t::SEARCH_FILTERS:
case ln_mode_t::SEARCH_FILES:
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
case ln_mode_t::CAPTURE:
rl_search_internal(rc, old_mode, true);
if (!rc->get_value().empty()) {

View File

@ -591,8 +591,10 @@ readline_context::save()
hs = nullptr;
}
readline_curses::readline_curses()
: rc_focus(noop_func{}), rc_change(noop_func{}), rc_perform(noop_func{}),
readline_curses::readline_curses(
std::shared_ptr<pollable_supervisor> supervisor)
: pollable(supervisor, pollable::category::interactive),
rc_focus(noop_func{}), rc_change(noop_func{}), rc_perform(noop_func{}),
rc_alt_perform(noop_func{}), rc_timeout(noop_func{}),
rc_abort(noop_func{}), rc_display_match(noop_func{}),
rc_display_next(noop_func{}), rc_blur(noop_func{}),
@ -1468,3 +1470,15 @@ readline_curses::set_attr_value(const attr_line_t& value)
this->rc_value_expiration = time(nullptr) + VALUE_EXPIRATION;
this->set_needs_update();
}
void
readline_curses::update_poll_set(std::vector<struct pollfd>& pollfds)
{
if (this->rc_pty[RCF_MASTER] != -1) {
pollfds.push_back((struct pollfd){this->rc_pty[RCF_MASTER], POLLIN, 0});
}
if (this->rc_command_pipe[RCF_MASTER] != -1) {
pollfds.push_back(
(struct pollfd){this->rc_command_pipe[RCF_MASTER], POLLIN, 0});
}
}

View File

@ -58,6 +58,7 @@
#include "base/result.h"
#include "help_text_formatter.hh"
#include "log_format.hh"
#include "pollable.hh"
#include "readline_context.hh"
#include "vt52_curses.hh"
@ -70,7 +71,9 @@ extern exec_context INIT_EXEC_CONTEXT;
* vt52 translation is done by the parent class, vt52_curses, while this class
* takes care of the communication between the two processes.
*/
class readline_curses : public vt52_curses {
class readline_curses
: public vt52_curses
, public pollable {
public:
using action = std::function<void(readline_curses*)>;
@ -85,9 +88,11 @@ public:
static const int VALUE_EXPIRATION = 20;
readline_curses();
readline_curses(std::shared_ptr<pollable_supervisor>);
~readline_curses() override;
using injectable = readline_curses(std::shared_ptr<pollable_supervisor>);
void add_context(int id, readline_context& rc)
{
this->rc_contexts[id] = &rc;
@ -156,16 +161,11 @@ public:
std::string get_alt_value() const { return this->rc_alt_value; }
void update_poll_set(std::vector<struct pollfd>& pollfds)
{
pollfds.push_back((struct pollfd){this->rc_pty[RCF_MASTER], POLLIN, 0});
pollfds.push_back(
(struct pollfd){this->rc_command_pipe[RCF_MASTER], POLLIN, 0});
}
void update_poll_set(std::vector<struct pollfd>& pollfds) override;
void handle_key(int ch);
void check_poll_set(const std::vector<struct pollfd>& pollfds);
void check_poll_set(const std::vector<struct pollfd>& pollfds) override;
void focus(int context,
const std::string& prompt,

397
src/spectro_impls.cc Normal file
View File

@ -0,0 +1,397 @@
/**
* Copyright (c) 2022, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "spectro_impls.hh"
#include "lnav.hh"
#include "logfile_sub_source.hh"
class filtered_sub_source : public text_sub_source {
public:
size_t text_line_count() override { return this->fss_lines.size(); }
void text_value_for_line(textview_curses& tc,
int line,
std::string& value_out,
line_flags_t flags) override
{
this->fss_delegate->text_value_for_line(
tc, this->fss_lines[line], value_out, flags);
}
size_t text_size_for_line(textview_curses& tc,
int line,
line_flags_t raw) override
{
return this->fss_delegate->text_size_for_line(
tc, this->fss_lines[line], raw);
}
void text_attrs_for_line(textview_curses& tc,
int line,
string_attrs_t& value_out) override
{
this->fss_delegate->text_attrs_for_line(
tc, this->fss_lines[line], value_out);
}
text_sub_source* fss_delegate;
std::vector<vis_line_t> fss_lines;
};
log_spectro_value_source::log_spectro_value_source(intern_string_t colname)
: lsvs_colname(colname)
{
this->update_stats();
}
void
log_spectro_value_source::update_stats()
{
auto& lss = lnav_data.ld_log_source;
this->lsvs_begin_time = 0;
this->lsvs_end_time = 0;
this->lsvs_stats.clear();
for (auto& ls : lss) {
auto* lf = ls->get_file_ptr();
if (lf == nullptr) {
continue;
}
auto format = lf->get_format();
const auto* stats = format->stats_for_value(this->lsvs_colname);
if (stats == nullptr) {
continue;
}
auto ll = lf->begin();
if (this->lsvs_begin_time == 0
|| ll->get_time() < this->lsvs_begin_time) {
this->lsvs_begin_time = ll->get_time();
}
ll = lf->end();
--ll;
if (ll->get_time() > this->lsvs_end_time) {
this->lsvs_end_time = ll->get_time();
}
this->lsvs_found = true;
this->lsvs_stats.merge(*stats);
}
if (this->lsvs_begin_time) {
time_t filtered_begin_time = lss.find_line(lss.at(0_vl))->get_time();
time_t filtered_end_time
= lss.find_line(lss.at(vis_line_t(lss.text_line_count() - 1)))
->get_time();
if (filtered_begin_time > this->lsvs_begin_time) {
this->lsvs_begin_time = filtered_begin_time;
}
if (filtered_end_time < this->lsvs_end_time) {
this->lsvs_end_time = filtered_end_time;
}
}
}
void
log_spectro_value_source::spectro_bounds(spectrogram_bounds& sb_out)
{
auto& lss = lnav_data.ld_log_source;
if (lss.text_line_count() == 0) {
return;
}
this->update_stats();
sb_out.sb_begin_time = this->lsvs_begin_time;
sb_out.sb_end_time = this->lsvs_end_time;
sb_out.sb_min_value_out = this->lsvs_stats.lvs_min_value;
sb_out.sb_max_value_out = this->lsvs_stats.lvs_max_value;
sb_out.sb_count = this->lsvs_stats.lvs_count;
}
void
log_spectro_value_source::spectro_row(spectrogram_request& sr,
spectrogram_row& row_out)
{
auto& lss = lnav_data.ld_log_source;
auto begin_line = lss.find_from_time(sr.sr_begin_time).value_or(0_vl);
auto end_line
= lss.find_from_time(sr.sr_end_time).value_or(lss.text_line_count());
for (const auto& msg_info : lss.window_at(begin_line, end_line)) {
const auto& ll = msg_info.get_logline();
if (ll.get_time() >= sr.sr_end_time) {
break;
}
const auto& values = msg_info.get_values();
auto lv_iter = find_if(values.begin(),
values.end(),
logline_value_cmp(&this->lsvs_colname));
if (lv_iter != values.end()) {
switch (lv_iter->lv_meta.lvm_kind) {
case value_kind_t::VALUE_FLOAT:
row_out.add_value(sr, lv_iter->lv_value.d, ll.is_marked());
break;
case value_kind_t::VALUE_INTEGER: {
row_out.add_value(sr, lv_iter->lv_value.i, ll.is_marked());
break;
}
default:
break;
}
}
}
row_out.sr_details_source_provider = [this](const spectrogram_request& sr,
double range_min,
double range_max) {
auto& lss = lnav_data.ld_log_source;
auto retval = std::make_unique<filtered_sub_source>();
auto begin_line = lss.find_from_time(sr.sr_begin_time).value_or(0_vl);
auto end_line = lss.find_from_time(sr.sr_end_time)
.value_or(lss.text_line_count());
retval->fss_delegate = &lss;
for (const auto& msg_info : lss.window_at(begin_line, end_line)) {
const auto& ll = msg_info.get_logline();
if (ll.get_time() >= sr.sr_end_time) {
break;
}
const auto& values = msg_info.get_values();
auto lv_iter = find_if(values.begin(),
values.end(),
logline_value_cmp(&this->lsvs_colname));
if (lv_iter != values.end()) {
switch (lv_iter->lv_meta.lvm_kind) {
case value_kind_t::VALUE_FLOAT:
if (range_min <= lv_iter->lv_value.d
&& lv_iter->lv_value.d < range_max) {
retval->fss_lines.emplace_back(
msg_info.get_vis_line());
}
break;
case value_kind_t::VALUE_INTEGER:
if (range_min <= lv_iter->lv_value.i
&& lv_iter->lv_value.i < range_max) {
retval->fss_lines.emplace_back(
msg_info.get_vis_line());
}
break;
default:
break;
}
}
}
return retval;
};
}
void
log_spectro_value_source::spectro_mark(textview_curses& tc,
time_t begin_time,
time_t end_time,
double range_min,
double range_max)
{
// XXX need to refactor this and the above method
auto& log_tc = lnav_data.ld_views[LNV_LOG];
auto& lss = lnav_data.ld_log_source;
vis_line_t begin_line = lss.find_from_time(begin_time).value_or(0_vl);
vis_line_t end_line
= lss.find_from_time(end_time).value_or(lss.text_line_count());
std::vector<logline_value> values;
string_attrs_t sa;
for (vis_line_t curr_line = begin_line; curr_line < end_line; ++curr_line) {
content_line_t cl = lss.at(curr_line);
std::shared_ptr<logfile> lf = lss.find(cl);
auto ll = lf->begin() + cl;
auto format = lf->get_format();
shared_buffer_ref sbr;
if (!ll->is_message()) {
continue;
}
lf->read_full_message(ll, sbr);
sa.clear();
values.clear();
format->annotate(cl, sbr, sa, values, false);
auto lv_iter = find_if(values.begin(),
values.end(),
logline_value_cmp(&this->lsvs_colname));
if (lv_iter != values.end()) {
switch (lv_iter->lv_meta.lvm_kind) {
case value_kind_t::VALUE_FLOAT:
if (range_min <= lv_iter->lv_value.d
&& lv_iter->lv_value.d <= range_max) {
log_tc.toggle_user_mark(&textview_curses::BM_USER,
curr_line);
}
break;
case value_kind_t::VALUE_INTEGER:
if (range_min <= lv_iter->lv_value.i
&& lv_iter->lv_value.i <= range_max) {
log_tc.toggle_user_mark(&textview_curses::BM_USER,
curr_line);
}
break;
default:
break;
}
}
}
}
db_spectro_value_source::db_spectro_value_source(std::string colname)
: dsvs_colname(std::move(colname))
{
this->update_stats();
}
void
db_spectro_value_source::update_stats()
{
this->dsvs_begin_time = 0;
this->dsvs_end_time = 0;
this->dsvs_stats.clear();
db_label_source& dls = lnav_data.ld_db_row_source;
stacked_bar_chart<std::string>& chart = dls.dls_chart;
date_time_scanner dts;
this->dsvs_column_index = dls.column_name_to_index(this->dsvs_colname);
if (!dls.has_log_time_column()) {
this->dsvs_error_msg
= "no 'log_time' column found or not in ascending order, "
"unable to create spectrogram";
return;
}
if (!this->dsvs_column_index) {
this->dsvs_error_msg = "unknown column -- " + this->dsvs_colname;
return;
}
if (!dls.dls_headers[this->dsvs_column_index.value()].hm_graphable) {
this->dsvs_error_msg = "column is not numeric -- " + this->dsvs_colname;
return;
}
if (dls.dls_rows.empty()) {
this->dsvs_error_msg = "empty result set";
return;
}
stacked_bar_chart<std::string>::bucket_stats_t bs
= chart.get_stats_for(this->dsvs_colname);
this->dsvs_begin_time = dls.dls_time_column.front().tv_sec;
this->dsvs_end_time = dls.dls_time_column.back().tv_sec;
this->dsvs_stats.lvs_min_value = bs.bs_min_value;
this->dsvs_stats.lvs_max_value = bs.bs_max_value;
this->dsvs_stats.lvs_count = dls.dls_rows.size();
}
void
db_spectro_value_source::spectro_bounds(spectrogram_bounds& sb_out)
{
auto& dls = lnav_data.ld_db_row_source;
if (dls.text_line_count() == 0) {
return;
}
this->update_stats();
sb_out.sb_begin_time = this->dsvs_begin_time;
sb_out.sb_end_time = this->dsvs_end_time;
sb_out.sb_min_value_out = this->dsvs_stats.lvs_min_value;
sb_out.sb_max_value_out = this->dsvs_stats.lvs_max_value;
sb_out.sb_count = this->dsvs_stats.lvs_count;
}
void
db_spectro_value_source::spectro_row(spectrogram_request& sr,
spectrogram_row& row_out)
{
auto& dls = lnav_data.ld_db_row_source;
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
auto end_row
= dls.row_for_time({sr.sr_end_time, 0}).value_or(dls.dls_rows.size());
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
double value = 0.0;
sscanf(
dls.dls_rows[lpc][this->dsvs_column_index.value()], "%lf", &value);
row_out.add_value(sr, value, false);
}
row_out.sr_details_source_provider = [this](const spectrogram_request& sr,
double range_min,
double range_max) {
auto& dls = lnav_data.ld_db_row_source;
auto retval = std::make_unique<filtered_sub_source>();
retval->fss_delegate = &dls;
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
auto end_row = dls.row_for_time({sr.sr_end_time, 0})
.value_or(dls.dls_rows.size());
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
double value = 0.0;
sscanf(dls.dls_rows[lpc][this->dsvs_column_index.value()],
"%lf",
&value);
if (range_min <= value && value < range_max) {
retval->fss_lines.emplace_back(lpc);
}
}
return retval;
};
}

87
src/spectro_impls.hh Normal file
View File

@ -0,0 +1,87 @@
/**
* Copyright (c) 2007-2022, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef lnav_spectro_impls_hh
#define lnav_spectro_impls_hh
#include "log_format.hh"
#include "spectro_source.hh"
class log_spectro_value_source : public spectrogram_value_source {
public:
log_spectro_value_source(intern_string_t colname);
void update_stats();
void spectro_bounds(spectrogram_bounds& sb_out) override;
void spectro_row(spectrogram_request& sr,
spectrogram_row& row_out) override;
void spectro_mark(textview_curses& tc,
time_t begin_time,
time_t end_time,
double range_min,
double range_max) override;
intern_string_t lsvs_colname;
logline_value_stats lsvs_stats;
time_t lsvs_begin_time{0};
time_t lsvs_end_time{0};
bool lsvs_found{false};
};
class db_spectro_value_source : public spectrogram_value_source {
public:
db_spectro_value_source(std::string colname);
void update_stats();
void spectro_bounds(spectrogram_bounds& sb_out) override;
void spectro_row(spectrogram_request& sr,
spectrogram_row& row_out) override;
void spectro_mark(textview_curses& tc,
time_t begin_time,
time_t end_time,
double range_min,
double range_max) override
{
}
std::string dsvs_colname;
logline_value_stats dsvs_stats;
time_t dsvs_begin_time{0};
time_t dsvs_end_time{0};
nonstd::optional<size_t> dsvs_column_index;
std::string dsvs_error_msg;
};
#endif

View File

@ -33,17 +33,38 @@
#include "base/ansi_scrubber.hh"
#include "base/math_util.hh"
#include "command_executor.hh"
#include "config.h"
nonstd::optional<size_t>
spectrogram_row::nearest_column(size_t current) const
{
nonstd::optional<size_t> retval;
nonstd::optional<size_t> nearest_distance;
for (size_t lpc = 0; lpc < this->sr_width; lpc++) {
if (this->sr_values[lpc].rb_counter == 0) {
continue;
}
auto curr_distance = abs_diff(lpc, current);
if (!retval || curr_distance < nearest_distance.value()) {
retval = lpc;
nearest_distance = abs_diff(lpc, current);
}
}
return retval;
}
bool
spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
{
switch (ch) {
case 'm': {
if (this->ss_cursor_top < 0
|| (size_t) this->ss_cursor_top >= this->text_line_count()
|| this->ss_cursor_column == -1
|| this->ss_value_source == nullptr)
auto sel = lv.get_selection();
if (sel < 0 || (size_t) sel >= this->text_line_count()
|| !this->ss_cursor_column || this->ss_value_source == nullptr)
{
alerter::singleton().chime();
return true;
@ -53,9 +74,10 @@ spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
vis_line_t height;
lv.get_dimensions(height, width);
width -= 2;
spectrogram_bounds& sb = this->ss_cached_bounds;
auto begin_time_opt = this->time_for_row(this->ss_cursor_top);
auto& sb = this->ss_cached_bounds;
auto begin_time_opt = this->time_for_row(sel);
if (!begin_time_opt) {
return true;
}
@ -67,9 +89,9 @@ spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
column_size = (sb.sb_max_value_out - sb.sb_min_value_out)
/ (double) (width - 1);
range_min
= sb.sb_min_value_out + this->ss_cursor_column * column_size;
range_max = range_min + column_size + column_size * 0.01;
range_min = sb.sb_min_value_out
+ this->ss_cursor_column.value_or(0) * column_size;
range_max = range_min + column_size;
this->ss_value_source->spectro_mark((textview_curses&) lv,
begin_time.tv_sec,
end_time.tv_sec,
@ -79,29 +101,30 @@ spectrogram_source::list_input_handle_key(listview_curses& lv, int ch)
lv.reload_data();
return true;
}
case KEY_LEFT:
case KEY_RIGHT: {
auto sel = lv.get_selection();
unsigned long width;
vis_line_t height;
string_attrs_t sa;
this->ss_cursor_top = lv.get_top();
lv.get_dimensions(height, width);
this->text_attrs_for_line(
(textview_curses&) lv, this->ss_cursor_top, sa);
this->text_attrs_for_line((textview_curses&) lv, sel, sa);
if (sa.empty()) {
this->ss_cursor_column = -1;
this->ss_cursor_column = nonstd::nullopt;
return true;
}
string_attrs_t::iterator current;
if (!this->ss_cursor_column) {
lv.set_selection(0_vl);
}
struct line_range lr(this->ss_cursor_column.value(),
this->ss_cursor_column.value() + 1);
struct line_range lr(this->ss_cursor_column,
this->ss_cursor_column + 1);
current = find_string_attr(sa, lr);
auto current = find_string_attr(sa, lr);
if (current != sa.end()) {
if (ch == KEY_LEFT) {
@ -141,27 +164,87 @@ spectrogram_source::list_value_for_overlay(const listview_curses& lv,
vis_line_t row,
attr_line_t& value_out)
{
if (y != 0) {
return false;
}
std::string& line = value_out.get_string();
char buf[128];
vis_line_t height;
unsigned long width;
lv.get_dimensions(height, width);
width -= 2;
if (y > 0) {
auto sel = lv.get_selection();
auto selected_y = sel - lv.get_top() + 2;
if (y == selected_y && this->ss_cursor_column) {
const auto& s_row = this->load_row(lv, sel);
const auto& bucket
= s_row.sr_values[this->ss_cursor_column.value()];
auto& sb = this->ss_cached_bounds;
spectrogram_request sr(sb);
auto sel_time = rounddown(sb.sb_begin_time, this->ss_granularity)
+ sel * this->ss_granularity;
sr.sr_width = width;
sr.sr_begin_time = sel_time;
sr.sr_end_time = sel_time + this->ss_granularity;
sr.sr_column_size = (sb.sb_max_value_out - sb.sb_min_value_out)
/ (double) (width - 1);
auto range_min = sb.sb_min_value_out
+ this->ss_cursor_column.value() * sr.sr_column_size;
auto range_max = range_min + sr.sr_column_size;
const auto desc = fmt::format(
FMT_STRING("{} value{} in the range {:.6Lg}-{:.6Lg} "),
bucket.rb_counter,
bucket.rb_counter == 1 ? "" : "s",
range_min,
range_max);
auto mark_offset = this->ss_cursor_column.value();
auto mark_is_before = true;
if (this->ss_cursor_column.value() + desc.size() > width) {
mark_offset -= desc.size();
mark_is_before = false;
}
value_out.append(mark_offset, ' ');
if (mark_is_before) {
value_out.append("\u25b2 ");
}
value_out.append(lnav::roles::comment(desc));
if (!mark_is_before) {
value_out.append("\u25b2 ");
}
if (this->ss_details_view != nullptr) {
if (s_row.sr_details_source_provider) {
auto row_details_source = s_row.sr_details_source_provider(
sr, range_min, range_max);
this->ss_details_view->set_sub_source(
row_details_source.get());
this->ss_details_source = std::move(row_details_source);
} else {
this->ss_details_view->set_sub_source(
this->ss_no_details_source);
}
}
return true;
}
return false;
}
auto& line = value_out.get_string();
char buf[128];
this->cache_bounds();
if (this->ss_cached_line_count == 0) {
value_out.with_ansi_string(ANSI_ROLE("error: no log data"),
role_t::VCR_ERROR);
value_out.append(lnav::roles::error("error: no log data"));
return true;
}
spectrogram_bounds& sb = this->ss_cached_bounds;
spectrogram_thresholds& st = this->ss_cached_thresholds;
auto& sb = this->ss_cached_bounds;
auto& st = this->ss_cached_thresholds;
snprintf(buf, sizeof(buf), "Min: %'.10lg", sb.sb_min_value_out);
line = buf;
@ -259,8 +342,7 @@ spectrogram_source::text_value_for_line(textview_curses& tc,
std::string& value_out,
text_sub_source::line_flags_t flags)
{
spectrogram_row& s_row = this->load_row(tc, row);
const auto& s_row = this->load_row(tc, row);
char tm_buffer[128];
struct tm tm;
@ -282,14 +364,6 @@ spectrogram_source::text_value_for_line(textview_curses& tc,
value_out[lpc] = 'x';
}
}
if (this->ss_cursor_top == row && this->ss_cursor_column != -1) {
if (value_out[this->ss_cursor_column] == 'x') {
value_out[this->ss_cursor_column] = '*';
} else {
value_out[this->ss_cursor_column] = '+';
}
}
}
void
@ -356,7 +430,7 @@ spectrogram_source::cache_bounds()
= (diff + this->ss_granularity - 1) / this->ss_granularity;
int64_t samples_per_row = sb.sb_count / this->ss_cached_line_count;
spectrogram_thresholds& st = this->ss_cached_thresholds;
auto& st = this->ss_cached_thresholds;
st.st_yellow_threshold = samples_per_row / 2;
st.st_green_threshold = st.st_yellow_threshold / 2;
@ -369,8 +443,8 @@ spectrogram_source::cache_bounds()
}
}
spectrogram_row&
spectrogram_source::load_row(textview_curses& tc, int row)
const spectrogram_row&
spectrogram_source::load_row(const listview_curses& tc, int row)
{
this->cache_bounds();
@ -380,12 +454,11 @@ spectrogram_source::load_row(textview_curses& tc, int row)
tc.get_dimensions(height, width);
width -= 2;
spectrogram_bounds& sb = this->ss_cached_bounds;
auto& sb = this->ss_cached_bounds;
spectrogram_request sr(sb);
time_t row_time;
sr.sr_width = width;
row_time = rounddown(sb.sb_begin_time, this->ss_granularity)
auto row_time = rounddown(sb.sb_begin_time, this->ss_granularity)
+ row * this->ss_granularity;
sr.sr_begin_time = row_time;
sr.sr_end_time = row_time + this->ss_granularity;
@ -393,17 +466,67 @@ spectrogram_source::load_row(textview_curses& tc, int row)
sr.sr_column_size
= (sb.sb_max_value_out - sb.sb_min_value_out) / (double) (width - 1);
spectrogram_row& s_row = this->ss_row_cache[row_time];
auto& s_row = this->ss_row_cache[row_time];
if (s_row.sr_values == nullptr || s_row.sr_width != width
if (s_row.sr_values.empty() || s_row.sr_width != width
|| s_row.sr_column_size != sr.sr_column_size)
{
s_row.sr_width = width;
s_row.sr_column_size = sr.sr_column_size;
delete[] s_row.sr_values;
s_row.sr_values = new spectrogram_row::row_bucket[width + 1];
s_row.sr_values.clear();
s_row.sr_values.resize(width + 1);
this->ss_value_source->spectro_row(sr, s_row);
}
return s_row;
}
bool
spectrogram_source::text_is_row_selectable(textview_curses& tc, vis_line_t row)
{
if (this->ss_value_source == nullptr) {
return false;
}
const auto& s_row = this->load_row(tc, row);
auto nearest_column
= s_row.nearest_column(this->ss_cursor_column.value_or(0));
return nearest_column.has_value();
}
void
spectrogram_source::text_selection_changed(textview_curses& tc)
{
if (this->ss_value_source == nullptr) {
return;
}
const auto& s_row = this->load_row(tc, tc.get_selection());
this->ss_cursor_column
= s_row.nearest_column(this->ss_cursor_column.value_or(0));
}
spectro_status_source::spectro_status_source()
{
this->sss_fields[F_TITLE].set_width(9);
this->sss_fields[F_TITLE].set_role(role_t::VCR_STATUS_TITLE);
this->sss_fields[F_TITLE].set_value(" Details ");
this->sss_fields[F_HELP].right_justify(true);
this->sss_fields[F_HELP].set_width(20);
this->sss_fields[F_HELP].set_value("Press " ANSI_BOLD("TAB") " to focus ");
this->sss_fields[F_HELP].set_left_pad(1);
}
size_t
spectro_status_source::statusview_fields()
{
return F_MAX;
}
status_field&
spectro_status_source::statusview_value_for_field(int field)
{
return this->sss_fields[field];
}

View File

@ -32,14 +32,17 @@
#ifndef spectro_source_hh
#define spectro_source_hh
#include <map>
#include <unordered_map>
#include <vector>
#include <math.h>
#include <time.h>
#include "statusview_curses.hh"
#include "textview_curses.hh"
struct exec_context;
struct spectrogram_bounds {
time_t sb_begin_time{0};
time_t sb_end_time{0};
@ -64,30 +67,33 @@ struct spectrogram_request {
};
struct spectrogram_row {
~spectrogram_row()
{
delete[] this->sr_values;
}
spectrogram_row() = default;
spectrogram_row(const spectrogram_row&) = delete;
struct row_bucket {
int rb_counter{0};
int rb_marks{0};
};
row_bucket* sr_values{nullptr};
std::vector<row_bucket> sr_values;
unsigned long sr_width{0};
double sr_column_size{0.0};
std::function<std::unique_ptr<text_sub_source>(
const spectrogram_request&, double range_min, double range_max)>
sr_details_source_provider;
void add_value(spectrogram_request& sr, double value, bool marked)
{
long index = lrint((value - sr.sr_bounds.sb_min_value_out)
/ sr.sr_column_size);
long index = std::floor((value - sr.sr_bounds.sb_min_value_out)
/ sr.sr_column_size);
this->sr_values[index].rb_counter += 1;
if (marked) {
this->sr_values[index].rb_marks += 1;
}
}
nonstd::optional<size_t> nearest_column(size_t current) const;
};
class spectrogram_value_source {
@ -119,7 +125,7 @@ public:
{
this->ss_cached_bounds.sb_count = 0;
this->ss_row_cache.clear();
this->ss_cursor_column = -1;
this->ss_cursor_column = nonstd::nullopt;
}
bool list_input_handle_key(listview_curses& lv, int ch) override;
@ -141,6 +147,10 @@ public:
return 0;
}
bool text_is_row_selectable(textview_curses& tc, vis_line_t row) override;
void text_selection_changed(textview_curses& tc) override;
nonstd::optional<struct timeval> time_for_row(vis_line_t row) override;
nonstd::optional<vis_line_t> row_for_time(
@ -157,16 +167,38 @@ public:
void cache_bounds();
spectrogram_row& load_row(textview_curses& tc, int row);
const spectrogram_row& load_row(const listview_curses& lv, int row);
textview_curses* ss_details_view;
text_sub_source* ss_no_details_source;
exec_context* ss_exec_context;
std::unique_ptr<text_sub_source> ss_details_source;
int ss_granularity{60};
spectrogram_value_source* ss_value_source{nullptr};
spectrogram_bounds ss_cached_bounds;
spectrogram_thresholds ss_cached_thresholds;
size_t ss_cached_line_count{0};
std::map<time_t, spectrogram_row> ss_row_cache;
vis_line_t ss_cursor_top;
int ss_cursor_column{-1};
std::unordered_map<time_t, spectrogram_row> ss_row_cache;
nonstd::optional<size_t> ss_cursor_column;
};
class spectro_status_source : public status_data_source {
public:
enum field_t {
F_TITLE,
F_HELP,
F_MAX
};
spectro_status_source();
size_t statusview_fields() override;
status_field& statusview_value_for_field(int field) override;
private:
status_field sss_fields[F_MAX];
};
#endif

View File

@ -33,6 +33,7 @@
#include "textview_curses.hh"
#include "base/ansi_scrubber.hh"
#include "base/injector.hh"
#include "base/time_util.hh"
#include "config.h"
#include "data_parser.hh"
@ -609,7 +610,8 @@ textview_curses::execute_search(const std::string& regex_orig)
highlight_map_t& hm = this->get_highlights();
hm[{highlight_source_t::PREVIEW, "search"}] = hl;
auto gp = std::make_unique<grep_proc<vis_line_t>>(code, *this);
auto gp = injector::get<std::shared_ptr<grep_proc<vis_line_t>>>(
code, *this);
gp->set_sink(this);
auto top = this->get_top();
@ -624,13 +626,14 @@ textview_curses::execute_search(const std::string& regex_orig)
}
gp->start();
this->tc_search_child = std::make_unique<grep_highlighter>(
this->tc_search_child = std::make_shared<grep_highlighter>(
gp, highlight_source_t::PREVIEW, "search", hm);
if (this->tc_sub_source != nullptr) {
this->tc_sub_source->get_grepper() | [this, code](auto pair) {
auto sgp = std::make_shared<grep_proc<vis_line_t>>(
code, *pair.first);
auto sgp
= injector::get<std::shared_ptr<grep_proc<vis_line_t>>>(
code, *pair.first);
sgp->set_sink(pair.second);
sgp->queue_request(0_vl);
@ -790,6 +793,39 @@ textview_curses::redo_search()
}
}
bool
textview_curses::listview_is_row_selectable(const listview_curses& lv,
vis_line_t row)
{
if (this->tc_sub_source != nullptr) {
return this->tc_sub_source->text_is_row_selectable(*this, row);
}
return true;
}
void
textview_curses::listview_selection_changed(const listview_curses& lv)
{
if (this->tc_sub_source != nullptr) {
this->tc_sub_source->text_selection_changed(*this);
}
}
textview_curses&
textview_curses::set_sub_source(text_sub_source* src)
{
if (this->tc_sub_source != src) {
this->tc_bookmarks.clear();
this->tc_sub_source = src;
if (src) {
src->register_view(this);
}
this->reload_data();
}
return *this;
}
void
text_time_translator::scroll_invoked(textview_curses* tc)
{

View File

@ -292,6 +292,13 @@ public:
virtual size_t text_line_width(textview_curses& curses) { return INT_MAX; }
virtual bool text_is_row_selectable(textview_curses& tc, vis_line_t row)
{
return true;
}
virtual void text_selection_changed(textview_curses& tc) {}
/**
* Get the value for a line.
*
@ -490,15 +497,7 @@ public:
void set_user_mark(const bookmark_type_t* bm, vis_line_t vl, bool marked);
textview_curses& set_sub_source(text_sub_source* src)
{
this->tc_sub_source = src;
if (src) {
src->register_view(this);
}
this->reload_data();
return *this;
}
textview_curses& set_sub_source(text_sub_source* src);
text_sub_source* get_sub_source() const { return this->tc_sub_source; }
@ -547,6 +546,10 @@ public:
void textview_value_for_row(vis_line_t line, attr_line_t& value_out);
bool listview_is_row_selectable(const listview_curses& lv, vis_line_t row);
void listview_selection_changed(const listview_curses& lv);
size_t listview_size_for_row(const listview_curses& lv, vis_line_t row)
{
return this->tc_sub_source->text_size_for_line(*this, row);
@ -669,26 +672,6 @@ public:
}
}
void update_poll_set(std::vector<struct pollfd>& pollfds)
{
if (this->tc_search_child) {
this->tc_search_child->get_grep_proc()->update_poll_set(pollfds);
}
if (this->tc_source_search_child) {
this->tc_source_search_child->update_poll_set(pollfds);
}
}
void check_poll_set(const std::vector<struct pollfd>& pollfds)
{
if (this->tc_search_child) {
this->tc_search_child->get_grep_proc()->check_poll_set(pollfds);
}
if (this->tc_source_search_child) {
this->tc_source_search_child->check_poll_set(pollfds);
}
}
std::string get_current_search() const
{
return this->tc_current_search;
@ -717,7 +700,7 @@ public:
protected:
class grep_highlighter {
public:
grep_highlighter(std::unique_ptr<grep_proc<vis_line_t>>& gp,
grep_highlighter(std::shared_ptr<grep_proc<vis_line_t>>& gp,
highlight_source_t source,
std::string hl_name,
highlight_map_t& hl_map)
@ -738,7 +721,7 @@ protected:
}
private:
std::unique_ptr<grep_proc<vis_line_t>> gh_grep_proc;
std::shared_ptr<grep_proc<vis_line_t>> gh_grep_proc;
highlight_source_t gh_hl_source;
std::string gh_hl_name;
highlight_map_t& gh_hl_map;
@ -768,7 +751,7 @@ protected:
std::string tc_current_search;
std::string tc_previous_search;
std::unique_ptr<grep_highlighter> tc_search_child;
std::shared_ptr<grep_highlighter> tc_search_child;
std::shared_ptr<grep_proc<vis_line_t>> tc_source_search_child;
};

View File

@ -535,8 +535,11 @@ layout_views()
bool preview_status_open
= !lnav_data.ld_preview_status_source.get_description().empty();
bool filter_status_open = false;
auto is_spectro = false;
lnav_data.ld_view_stack.top() | [&](auto tc) {
is_spectro = (tc == &lnav_data.ld_views[LNV_SPECTRO]);
text_sub_source* tss = tc->get_sub_source();
if (tss == nullptr) {
@ -601,7 +604,8 @@ layout_views()
int bottom_height = (doc_open ? 1 : 0) + doc_height
+ (preview_status_open ? 1 : 0) + preview_height + 1 // bottom status
+ match_height + um_height + lnav_data.ld_rl_view->get_height();
+ match_height + um_height + lnav_data.ld_rl_view->get_height()
+ (is_spectro && !doc_open ? 5 : 0);
for (auto& tc : lnav_data.ld_views) {
tc.set_height(vis_line_t(-(bottom_height + (filter_status_open ? 1 : 0)
@ -622,6 +626,10 @@ layout_views()
lnav_data.ld_status[LNS_PREVIEW].set_top(height - bottom_height
+ (doc_open ? 1 : 0) + doc_height);
lnav_data.ld_status[LNS_PREVIEW].set_visible(preview_status_open);
lnav_data.ld_status[LNS_SPECTRO].set_top(height - bottom_height - 1);
lnav_data.ld_status[LNS_SPECTRO].set_visible(is_spectro);
lnav_data.ld_status[LNS_SPECTRO].set_enabled(lnav_data.ld_mode
== ln_mode_t::SPECTRO_DETAILS);
if (!doc_open || doc_side_by_side) {
lnav_data.ld_doc_view.set_height(vis_line_t(doc_height));
@ -656,6 +664,13 @@ layout_views()
+ (doc_open ? 1 : 0) + doc_height);
lnav_data.ld_user_message_view.set_y(
height - lnav_data.ld_rl_view->get_height() - match_height - um_height);
lnav_data.ld_spectro_details_view.set_y(height - bottom_height);
lnav_data.ld_spectro_details_view.set_height(
is_spectro && !doc_open ? 5_vl : 0_vl);
lnav_data.ld_spectro_details_view.set_width(width);
lnav_data.ld_spectro_details_view.set_title("spectro-details");
lnav_data.ld_match_view.set_y(height - lnav_data.ld_rl_view->get_height()
- match_height);
lnav_data.ld_rl_view->set_width(width);
@ -893,9 +908,9 @@ next_cluster(nonstd::optional<vis_line_t> (bookmark_vector<vis_line_t>::*f)(
const bookmark_type_t* bt,
const vis_line_t top)
{
textview_curses* tc = get_textview_for_mode(lnav_data.ld_mode);
vis_bookmarks& bm = tc->get_bookmarks();
bookmark_vector<vis_line_t>& bv = bm[bt];
auto* tc = get_textview_for_mode(lnav_data.ld_mode);
auto& bm = tc->get_bookmarks();
auto& bv = bm[bt];
bool top_is_marked = binary_search(bv.begin(), bv.end(), top);
vis_line_t last_top(top), tc_height;
nonstd::optional<vis_line_t> new_top = top;
@ -1009,7 +1024,7 @@ search_forward_from(textview_curses* tc)
{
vis_line_t height,
retval = tc->is_selectable() ? tc->get_selection() : tc->get_top();
key_repeat_history& krh = lnav_data.ld_key_repeat_history;
auto& krh = lnav_data.ld_key_repeat_history;
unsigned long width;
tc->get_dimensions(height, width);
@ -1031,6 +1046,9 @@ get_textview_for_mode(ln_mode_t mode)
case ln_mode_t::SEARCH_FILES:
case ln_mode_t::FILES:
return &lnav_data.ld_files_view;
case ln_mode_t::SPECTRO_DETAILS:
case ln_mode_t::SEARCH_SPECTRO_DETAILS:
return &lnav_data.ld_spectro_details_view;
default:
return *lnav_data.ld_view_stack.top();
}

View File

@ -61,6 +61,8 @@ enum class ln_mode_t : int {
BREADCRUMBS,
FILTER,
FILES,
SPECTRO_DETAILS,
SEARCH_SPECTRO_DETAILS,
COMMAND,
SEARCH,
SEARCH_FILTERS,

View File

@ -70,7 +70,7 @@ public:
retval = read_result.isOk();
}
}
} catch (line_buffer::error& e) {
} catch (const line_buffer::error& e) {
fprintf(stderr,
"error: source buffer error %d %s\n",
this->ms_buffer.get_fd(),
@ -87,7 +87,7 @@ private:
class my_sink : public grep_proc_sink<vis_line_t> {
public:
my_sink() : ms_finished(false){};
my_sink() : ms_finished(false) {}
void grep_match(grep_proc<vis_line_t>& gp,
vis_line_t line,
@ -95,7 +95,7 @@ public:
int end)
{
printf("%d:%d:%d\n", (int) line, start, end);
};
}
void grep_capture(grep_proc<vis_line_t>& gp,
vis_line_t line,
@ -104,12 +104,9 @@ public:
char* capture)
{
fprintf(stderr, "%d(%d:%d)%s\n", (int) line, start, end, capture);
};
}
void grep_end(grep_proc<vis_line_t>& gp)
{
this->ms_finished = true;
};
void grep_end(grep_proc<vis_line_t>& gp) { this->ms_finished = true; }
bool ms_finished;
};
@ -135,10 +132,11 @@ main(int argc, char* argv[])
{
fprintf(stderr, "error: invalid pattern -- %s\n", errptr);
} else {
auto psuperv = std::make_shared<pollable_supervisor>();
my_source ms(fd);
my_sink msink;
grep_proc<vis_line_t> gp(code, ms);
grep_proc<vis_line_t> gp(code, ms, psuperv);
gp.set_sink(&msink);
gp.queue_request();
@ -147,10 +145,10 @@ main(int argc, char* argv[])
while (!msink.ms_finished) {
vector<struct pollfd> pollfds;
gp.update_poll_set(pollfds);
psuperv->update_poll_set(pollfds);
poll(&pollfds[0], pollfds.size(), -1);
gp.check_poll_set(pollfds);
psuperv->check_poll_set(pollfds);
}
}

View File

@ -92,8 +92,9 @@ main(int argc, char* argv[])
}
}
auto psuperv = std::make_shared<pollable_supervisor>();
readline_context context("test", &COMMANDS);
readline_curses rlc;
readline_curses rlc(psuperv);
rlc.add_context(1, context);
rlc.start();
@ -119,7 +120,7 @@ main(int argc, char* argv[])
int rc;
pollfds.push_back((struct pollfd){STDIN_FILENO, POLLIN, 0});
rlc.update_poll_set(pollfds);
psuperv->update_poll_set(pollfds);
rlc.do_update();
refresh();
@ -145,7 +146,7 @@ main(int argc, char* argv[])
}
}
}
rlc.check_poll_set(pollfds);
psuperv->check_poll_set(pollfds);
}
}

View File

@ -384,14 +384,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_sql.sh_13429aed81d7edfd47b57e9cdb8a25c43aff35c4.out \
$(srcdir)/%reldir%/test_sql.sh_1cbb81cfe40ee16332c5c775a74d06b945aa65c2.err \
$(srcdir)/%reldir%/test_sql.sh_1cbb81cfe40ee16332c5c775a74d06b945aa65c2.out \
$(srcdir)/%reldir%/test_sql.sh_1ed6e8b3194bf16477cce9542fe1d9750560cbd1.err \
$(srcdir)/%reldir%/test_sql.sh_1ed6e8b3194bf16477cce9542fe1d9750560cbd1.out \
$(srcdir)/%reldir%/test_sql.sh_1fb2a7366f0f271643e0ee30d76e03b643ff7dce.err \
$(srcdir)/%reldir%/test_sql.sh_1fb2a7366f0f271643e0ee30d76e03b643ff7dce.out \
$(srcdir)/%reldir%/test_sql.sh_2532083f215ed44630621f18df3dd7b77c06ae10.err \
$(srcdir)/%reldir%/test_sql.sh_2532083f215ed44630621f18df3dd7b77c06ae10.out \
$(srcdir)/%reldir%/test_sql.sh_264a70cabc0609e47d9e39b23376321c9aec6e23.err \
$(srcdir)/%reldir%/test_sql.sh_264a70cabc0609e47d9e39b23376321c9aec6e23.out \
$(srcdir)/%reldir%/test_sql.sh_26c0d94d7837792144f2d0f866fb3c12a0bd410d.err \
$(srcdir)/%reldir%/test_sql.sh_26c0d94d7837792144f2d0f866fb3c12a0bd410d.out \
$(srcdir)/%reldir%/test_sql.sh_28e23f4e98b1acd6478e39844fd9306b444550c3.err \
@ -408,8 +402,6 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_sql.sh_31df37f254255115611fc321b63374a2fa4a1cd5.out \
$(srcdir)/%reldir%/test_sql.sh_3d77a2092192caf98e141a6039e886ede836f044.err \
$(srcdir)/%reldir%/test_sql.sh_3d77a2092192caf98e141a6039e886ede836f044.out \
$(srcdir)/%reldir%/test_sql.sh_3f6b92e7948d40b9d2df06112f30ce899bb0d7f5.err \
$(srcdir)/%reldir%/test_sql.sh_3f6b92e7948d40b9d2df06112f30ce899bb0d7f5.out \
$(srcdir)/%reldir%/test_sql.sh_4090f96ea11a344c1e2939211da778992dab47d8.err \
$(srcdir)/%reldir%/test_sql.sh_4090f96ea11a344c1e2939211da778992dab47d8.out \
$(srcdir)/%reldir%/test_sql.sh_4629b626c65a85d7a5595571e195b67afca272ba.err \

View File

@ -1,3 +1,4 @@
Min: 0   1-23   24-48   49+ Max: 291690
 Thu Nov 03 00:15:00               
 Thu Nov 03 00:20:00
Min: 0   1-23   24-48   49+ Max: 291690
 Thu Nov 03 00:15:00               
70 values in the range 0-3788.18 
 Thu Nov 03 00:20:00

View File

@ -124,9 +124,10 @@ main(int argc, char* argv[])
pcre_refcount(code, 1);
assert(code != NULL);
auto psuperv = std::make_shared<pollable_supervisor>();
{
my_source ms;
grep_proc<vis_line_t> gp(code, ms);
grep_proc<vis_line_t> gp(code, ms, psuperv);
gp.queue_request(10_vl, 14_vl);
gp.queue_request(0_vl, 3_vl);
@ -136,7 +137,8 @@ main(int argc, char* argv[])
{
my_sleeper_source mss;
grep_proc<vis_line_t>* gp = new grep_proc<vis_line_t>(code, mss);
grep_proc<vis_line_t>* gp
= new grep_proc<vis_line_t>(code, mss, psuperv);
int status;
gp->queue_request();

View File

@ -32,6 +32,7 @@
#include "config.h"
#include "lnav.hh"
#include "service_tags.hh"
#include "spectro_source.hh"
struct lnav_data_t lnav_data;