Adopt the new termsize

This eliminates the C++ version.
This commit is contained in:
ridiculousfish 2023-03-19 15:50:33 -07:00
parent 6ec35ce182
commit 732f7284d4
13 changed files with 94 additions and 368 deletions

View file

@ -123,7 +123,7 @@ set(FISH_SRCS
src/pager.cpp src/parse_execution.cpp src/parse_tree.cpp src/parse_util.cpp
src/parser.cpp src/parser_keywords.cpp src/path.cpp src/postfork.cpp
src/proc.cpp src/re.cpp src/reader.cpp src/screen.cpp
src/signals.cpp src/termsize.cpp src/tinyexpr.cpp
src/signals.cpp src/tinyexpr.cpp
src/trace.cpp src/utf8.cpp
src/wcstringutil.cpp src/wgetopt.cpp src/wildcard.cpp
src/wutil.cpp src/fds.cpp src/rustffi.cpp

View file

@ -35,6 +35,7 @@ fn main() -> miette::Result<()> {
"src/parse_constants.rs",
"src/redirection.rs",
"src/smoke.rs",
"src/termsize.rs",
"src/timer.rs",
"src/tokenizer.rs",
"src/topic_monitor.rs",

View file

@ -14,11 +14,10 @@ use widestring_suffix::widestrs;
use crate::builtins::shared::io_streams_t;
use crate::common::{escape_string, replace_with, EscapeFlags, EscapeStringStyle, ScopeGuard};
use crate::ffi::{
self, block_t, parser_t, signal_check_cancel, signal_handle, termsize_container_t, Repin,
};
use crate::ffi::{self, block_t, parser_t, signal_check_cancel, signal_handle, Repin};
use crate::flog::FLOG;
use crate::signal::{sig2wcs, signal_get_desc};
use crate::termsize;
use crate::wchar::{wstr, WString, L};
use crate::wchar_ffi::{wcharz_t, AsWstr, WCharFromFFI, WCharToFFI};
use crate::wutil::sprintf;
@ -783,7 +782,7 @@ pub fn fire_delayed(parser: &mut parser_t) {
// HACK: The only variables we change in response to a *signal* are $COLUMNS and $LINES.
// Do that now.
if sig == libc::SIGWINCH {
termsize_container_t::ffi_updating(parser.pin()).within_unique_ptr();
termsize::SHARED_CONTAINER.updating(parser);
}
let event = Event {
desc: EventDescription {

View file

@ -36,7 +36,6 @@ include_cpp! {
#include "tokenizer.h"
#include "wildcard.h"
#include "wutil.h"
#include "termsize.h"
// We need to block these types so when exposing C++ to Rust.
block!("WaitHandleStoreFFI")
@ -111,7 +110,6 @@ include_cpp! {
generate!("statuses_t")
generate!("io_chain_t")
generate!("termsize_container_t")
generate!("env_var_t")
}

View file

@ -10,6 +10,30 @@ use crate::wutil::fish_wcstoi;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Mutex;
#[cxx::bridge]
mod termsize_ffi {
#[cxx_name = "termsize_t"]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Termsize {
/// Width of the terminal, in columns.
pub width: isize,
/// Height of the terminal, in rows.
pub height: isize,
}
extern "Rust" {
pub fn termsize_default() -> Termsize;
pub fn termsize_last() -> Termsize;
pub fn termsize_initialize_ffi(vars: *const u8) -> Termsize;
pub fn termsize_invalidate_tty();
pub fn handle_columns_lines_var_change_ffi(vars: *const u8);
pub fn termsize_update_ffi(parser: *mut u8) -> Termsize;
pub fn termsize_handle_winch();
}
}
use termsize_ffi::Termsize;
// A counter which is incremented every SIGWINCH, or when the tty is otherwise invalidated.
static TTY_TERMSIZE_GEN_COUNT: AtomicU32 = AtomicU32::new(0);
@ -58,15 +82,6 @@ fn read_termsize_from_tty() -> Option<Termsize> {
ret
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Termsize {
/// Width of the terminal, in columns.
pub width: isize,
/// Height of the terminal, in rows.
pub height: isize,
}
impl Termsize {
/// Default width and height.
pub const DEFAULT_WIDTH: isize = 80;
@ -150,7 +165,7 @@ impl TermsizeContainer {
/// Initialize our termsize, using the given environment stack.
/// This will prefer to use COLUMNS and LINES, but will fall back to the tty size reader.
/// This does not change any variables in the environment.
pub fn initialize(&mut self, vars: &environment_t) -> Termsize {
pub fn initialize(&self, vars: &environment_t) -> Termsize {
let new_termsize = Termsize {
width: var_to_int_or(vars.get_as_string_flags(L!("COLUMNS"), EnvMode::GLOBAL), -1),
height: var_to_int_or(vars.get_as_string_flags(L!("LINES"), EnvMode::GLOBAL), -1),
@ -166,10 +181,11 @@ impl TermsizeContainer {
data.current()
}
/// If our termsize is stale, update it, using \p parser firing any events that may be
/// If our termsize is stale, update it, using \p parser to fire any events that may be
/// registered for COLUMNS and LINES.
/// This requires a shared reference so it can work from a static.
/// \return the updated termsize.
pub fn updating(&mut self, parser: &mut parser_t) -> Termsize {
pub fn updating(&self, parser: &mut parser_t) -> Termsize {
let new_size;
let prev_size;
@ -198,7 +214,7 @@ impl TermsizeContainer {
new_size
}
fn set_columns_lines_vars(&mut self, val: Termsize, parser: &mut parser_t) {
fn set_columns_lines_vars(&self, val: Termsize, parser: &mut parser_t) {
let saved = self.setting_env_vars.swap(true, Ordering::Relaxed);
parser.pin().set_var_and_fire(
&L!("COLUMNS").to_ffi(),
@ -249,7 +265,7 @@ impl TermsizeContainer {
}
}
static SHARED_CONTAINER: TermsizeContainer = TermsizeContainer {
pub static SHARED_CONTAINER: TermsizeContainer = TermsizeContainer {
data: Mutex::new(TermsizeData::defaults()),
setting_env_vars: AtomicBool::new(false),
tty_size_reader: read_termsize_from_tty,
@ -257,11 +273,48 @@ static SHARED_CONTAINER: TermsizeContainer = TermsizeContainer {
const _: () = assert_sync::<TermsizeContainer>();
/// Helper to return the default termsize.
pub fn termsize_default() -> Termsize {
Termsize::defaults()
}
/// Convenience helper to return the last known termsize.
pub fn termsize_last() -> Termsize {
return SHARED_CONTAINER.last();
}
/// Called when the COLUMNS or LINES variables are changed.
/// The pointer is to an environment_t, but has the wrong type to satisfy cxx.
pub fn handle_columns_lines_var_change_ffi(vars_ptr: *const u8) {
assert!(!vars_ptr.is_null());
let vars: &environment_t = unsafe { &*(vars_ptr as *const environment_t) };
SHARED_CONTAINER.handle_columns_lines_var_change(vars);
}
/// Called to initialize the termsize.
/// The pointer is to an environment_t, but has the wrong type to satisfy cxx.
pub fn termsize_initialize_ffi(vars_ptr: *const u8) -> Termsize {
assert!(!vars_ptr.is_null());
let vars: &environment_t = unsafe { &*(vars_ptr as *const environment_t) };
SHARED_CONTAINER.initialize(vars)
}
/// Called to update termsize.
pub fn termsize_update_ffi(parser_ptr: *mut u8) -> Termsize {
assert!(!parser_ptr.is_null());
let parser: &mut parser_t = unsafe { &mut *(parser_ptr as *mut parser_t) };
SHARED_CONTAINER.updating(parser)
}
/// FFI bridge for WINCH.
pub fn termsize_handle_winch() {
TermsizeContainer::handle_winch();
}
pub fn termsize_invalidate_tty() {
TermsizeContainer::invalidate_tty();
}
use crate::ffi_tests::add_test;
add_test!("test_termsize", || {
let env_global = EnvMode::GLOBAL;
@ -272,7 +325,7 @@ add_test!("test_termsize", || {
fn stubby_termsize() -> Option<Termsize> {
*STUBBY_TERMSIZE.lock().unwrap()
}
let mut ts = TermsizeContainer {
let ts = TermsizeContainer {
data: Mutex::new(TermsizeData::defaults()),
setting_env_vars: AtomicBool::new(false),
tty_size_reader: stubby_termsize,
@ -325,7 +378,7 @@ add_test!("test_termsize", || {
assert_eq!(ts.last(), Termsize::new(83, 38));
// initialize() even beats the tty reader until a sigwinch.
let mut ts2 = TermsizeContainer {
let ts2 = TermsizeContainer {
data: Mutex::new(TermsizeData::defaults()),
setting_env_vars: AtomicBool::new(false),
tty_size_reader: stubby_termsize,

View file

@ -417,7 +417,8 @@ void env_init(const struct config_paths_t *paths, bool do_uvars, bool default_pa
}
// Initialize termsize variables.
auto termsize = termsize_container_t::shared().initialize(vars);
environment_t &env_vars = vars;
auto termsize = termsize_initialize_ffi(reinterpret_cast<const unsigned char *>(&env_vars));
if (vars.get(L"COLUMNS").missing_or_empty())
vars.set_one(L"COLUMNS", ENV_GLOBAL, to_string(termsize.width));
if (vars.get(L"LINES").missing_or_empty())

View file

@ -207,7 +207,9 @@ static void handle_change_ambiguous_width(const env_stack_t &vars) {
}
static void handle_term_size_change(const env_stack_t &vars) {
termsize_container_t::shared().handle_columns_lines_var_change(vars);
// Need to use a pointer to send this through cxx ffi.
const environment_t &env_vars = vars;
handle_columns_lines_var_change_ffi(reinterpret_cast<const unsigned char *>(&env_vars));
}
static void handle_fish_history_change(const env_stack_t &vars) {

View file

@ -2402,7 +2402,7 @@ static void test_pager_navigation() {
pager_t pager;
pager.set_completions(completions);
pager.set_term_size(termsize_t::defaults());
pager.set_term_size(termsize_default());
page_rendering_t render = pager.render();
if (render.term_width != 80) err(L"Wrong term width");
@ -6715,72 +6715,6 @@ static void test_re_substitute() {
}
} // namespace
struct termsize_tester_t {
static void test();
};
void termsize_tester_t::test() {
say(L"Testing termsize");
parser_t &parser = parser_t::principal_parser();
env_stack_t &vars = parser.vars();
// Use a static variable so we can pretend we're the kernel exposing a terminal size.
static maybe_t<termsize_t> stubby_termsize{};
termsize_container_t ts([] { return stubby_termsize; });
// Initially default value.
do_test(ts.last() == termsize_t::defaults());
// Haha we change the value, it doesn't even know.
stubby_termsize = termsize_t{42, 84};
do_test(ts.last() == termsize_t::defaults());
// Ok let's tell it. But it still doesn't update right away.
ts.handle_winch();
do_test(ts.last() == termsize_t::defaults());
// Ok now we tell it to update.
ts.updating(parser);
do_test(ts.last() == *stubby_termsize);
do_test(vars.get(L"COLUMNS")->as_string() == L"42");
do_test(vars.get(L"LINES")->as_string() == L"84");
// Wow someone set COLUMNS and LINES to a weird value.
// Now the tty's termsize doesn't matter.
vars.set(L"COLUMNS", ENV_GLOBAL, {L"75"});
vars.set(L"LINES", ENV_GLOBAL, {L"150"});
ts.handle_columns_lines_var_change(vars);
do_test(ts.last() == termsize_t(75, 150));
do_test(vars.get(L"COLUMNS")->as_string() == L"75");
do_test(vars.get(L"LINES")->as_string() == L"150");
vars.set(L"COLUMNS", ENV_GLOBAL, {L"33"});
ts.handle_columns_lines_var_change(vars);
do_test(ts.last() == termsize_t(33, 150));
// Oh it got SIGWINCH, now the tty matters again.
ts.handle_winch();
do_test(ts.last() == termsize_t(33, 150));
do_test(ts.updating(parser) == *stubby_termsize);
do_test(vars.get(L"COLUMNS")->as_string() == L"42");
do_test(vars.get(L"LINES")->as_string() == L"84");
// Test initialize().
vars.set(L"COLUMNS", ENV_GLOBAL, {L"83"});
vars.set(L"LINES", ENV_GLOBAL, {L"38"});
ts.initialize(vars);
do_test(ts.last() == termsize_t(83, 38));
// initialize() even beats the tty reader until a sigwinch.
termsize_container_t ts2([] { return stubby_termsize; });
ts.initialize(vars);
ts2.updating(parser);
do_test(ts.last() == termsize_t(83, 38));
ts2.handle_winch();
do_test(ts2.updating(parser) == *stubby_termsize);
}
void test_wgetopt() {
// Regression test for a crash.
const wchar_t *const short_options = L"-a";
@ -6938,7 +6872,6 @@ static const test_t s_tests[]{
{TEST_GROUP("topics"), test_topic_monitor_torture},
{TEST_GROUP("pipes"), test_pipes},
{TEST_GROUP("fd_event"), test_fd_event_signaller},
{TEST_GROUP("termsize"), termsize_tester_t::test},
{TEST_GROUP("killring"), test_killring},
{TEST_GROUP("re"), test_re_errs},
{TEST_GROUP("re"), test_re_basic},

View file

@ -931,7 +931,7 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
void delete_char(bool backward = true);
/// Called to update the termsize, including $COLUMNS and $LINES, as necessary.
void update_termsize() { (void)termsize_container_t::shared().updating(parser()); }
void update_termsize() { termsize_update_ffi(reinterpret_cast<unsigned char *>(&parser())); }
// Import history from older location (config path) if our current history is empty.
void import_history_if_necessary();
@ -1064,7 +1064,7 @@ static void term_steal() {
}
}
termsize_container_t::shared().invalidate_tty();
termsize_invalidate_tty();
}
bool fish_is_unwinding_for_exit() {
@ -1292,7 +1292,7 @@ static history_pager_result_t history_pager_search(const std::shared_ptr<history
// We can still push fish further upward in case the first entry is multiline,
// but that can't really be helped.
// (subtract 2 for the search line and the prompt)
size_t page_size = std::max(termsize_last().height / 2 - 2, 12);
size_t page_size = std::max(termsize_last().height / 2 - 2, (rust::isize)12);
completion_list_t completions;
history_search_t search{history, search_string, history_search_type_t::contains,
@ -2587,7 +2587,7 @@ static void reader_interactive_init(parser_t &parser) {
}
}
termsize_container_t::shared().invalidate_tty();
termsize_invalidate_tty();
// Provide value for `status current-command`
parser.libdata().status_vars.command = L"fish";
@ -3353,7 +3353,7 @@ void reader_data_t::run_input_command_scripts(const wcstring_list_t &cmds) {
if (res < 0) {
wperror(L"tcsetattr");
}
termsize_container_t::shared().invalidate_tty();
termsize_invalidate_tty();
}
/// Read normal characters, inserting them into the command line.

View file

@ -1209,8 +1209,9 @@ void screen_t::write(const wcstring &left_prompt, const wcstring &right_prompt,
// Re-render our completions page if necessary. Limit the term size of the pager to the true
// term size, minus the number of lines consumed by our string.
pager.set_term_size(termsize_t{std::max(1, curr_termsize.width),
std::max(1, curr_termsize.height - full_line_count)});
pager.set_term_size(
termsize_t{std::max((rust::isize)1, curr_termsize.width),
std::max((rust::isize)1, curr_termsize.height - full_line_count)});
pager.update_rendering(&page_rendering);
// Append pager_data (none if empty).
this->desired.append_lines(page_rendering.screen_data);

View file

@ -234,7 +234,7 @@ static void fish_signal_handler(int sig, siginfo_t *info, void *context) {
#ifdef SIGWINCH
case SIGWINCH:
// Respond to a winch signal by telling the termsize container.
termsize_container_t::handle_winch();
termsize_handle_winch();
break;
#endif

View file

@ -1,155 +0,0 @@
// Support for exposing the terminal size.
#include "termsize.h"
#include <unistd.h>
#include <cerrno>
#include <climits>
#include "env.h"
#include "flog.h"
#include "maybe.h"
#include "parser.h"
#include "wcstringutil.h"
#include "wutil.h"
#ifdef HAVE_WINSIZE
#include <sys/ioctl.h>
#include <termios.h>
#endif
// A counter which is incremented every SIGWINCH, or when the tty is otherwise invalidated.
static volatile uint32_t s_tty_termsize_gen_count{0};
/// \return a termsize from ioctl, or none on error or if not supported.
static maybe_t<termsize_t> read_termsize_from_tty() {
maybe_t<termsize_t> result{};
#ifdef HAVE_WINSIZE
struct winsize winsize = {0, 0, 0, 0};
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) >= 0) {
// 0 values are unusable, fall back to the default instead.
if (winsize.ws_col == 0) {
FLOGF(term_support, L"Terminal has 0 columns, falling back to default width");
winsize.ws_col = termsize_t::DEFAULT_WIDTH;
}
if (winsize.ws_row == 0) {
FLOGF(term_support, L"Terminal has 0 rows, falling back to default height");
winsize.ws_row = termsize_t::DEFAULT_HEIGHT;
}
result = termsize_t(winsize.ws_col, winsize.ws_row);
}
#endif
return result;
}
// static
termsize_container_t &termsize_container_t::shared() {
// Heap-allocated to avoid runtime dtor registration.
static auto *res = new termsize_container_t(read_termsize_from_tty);
return *res;
}
termsize_t termsize_container_t::ffi_updating(parser_t &parser) {
return shared().updating(parser);
}
termsize_t termsize_container_t::data_t::current() const {
// This encapsulates our ordering logic. If we have a termsize from a tty, use it; otherwise use
// what we have seen from the environment.
if (this->last_from_tty) return *this->last_from_tty;
if (this->last_from_env) return *this->last_from_env;
return termsize_t::defaults();
}
void termsize_container_t::data_t::mark_override_from_env(termsize_t ts) {
// Here we pretend to have an up-to-date tty value so that we will prefer the environment value.
this->last_from_env = ts;
this->last_from_tty.reset();
this->last_tty_gen_count = s_tty_termsize_gen_count;
}
termsize_t termsize_container_t::last() const { return this->data_.acquire()->current(); }
termsize_t termsize_container_t::updating(parser_t &parser) {
termsize_t new_size = termsize_t::defaults();
termsize_t prev_size = termsize_t::defaults();
// Take the lock in a local region.
// Capture the size before and the new size.
{
auto data = data_.acquire();
prev_size = data->current();
// Critical read of signal-owned variable.
// This must happen before the TIOCGWINSZ ioctl.
const uint32_t tty_gen_count = s_tty_termsize_gen_count;
if (data->last_tty_gen_count != tty_gen_count) {
// Our idea of the size of the terminal may be stale.
// Apply any updates.
data->last_tty_gen_count = tty_gen_count;
data->last_from_tty = this->tty_size_reader_();
}
new_size = data->current();
}
// Announce any updates.
if (new_size != prev_size) set_columns_lines_vars(new_size, parser);
return new_size;
}
void termsize_container_t::set_columns_lines_vars(termsize_t val, parser_t &parser) {
const bool saved = setting_env_vars_;
setting_env_vars_ = true;
parser.set_var_and_fire(L"COLUMNS", ENV_GLOBAL, to_string(val.width));
parser.set_var_and_fire(L"LINES", ENV_GLOBAL, to_string(val.height));
setting_env_vars_ = saved;
}
/// Convert an environment variable to an int, or return a default value.
/// The int must be >0 and <USHRT_MAX (from struct winsize).
static int var_to_int_or(const maybe_t<env_var_t> &var, int def) {
if (var.has_value() && !var->empty()) {
errno = 0;
int proposed = fish_wcstoi(var->as_string().c_str());
if (errno == 0 && proposed > 0 && proposed <= USHRT_MAX) {
return proposed;
}
}
return def;
}
termsize_t termsize_container_t::initialize(const environment_t &vars) {
termsize_t new_termsize{
var_to_int_or(vars.get(L"COLUMNS", ENV_GLOBAL), -1),
var_to_int_or(vars.get(L"LINES", ENV_GLOBAL), -1),
};
auto data = data_.acquire();
if (new_termsize.width > 0 && new_termsize.height > 0) {
data->mark_override_from_env(new_termsize);
} else {
data->last_tty_gen_count = s_tty_termsize_gen_count;
data->last_from_tty = this->tty_size_reader_();
}
return data->current();
}
void termsize_container_t::handle_columns_lines_var_change(const environment_t &vars) {
// Do nothing if we are the ones setting it.
if (setting_env_vars_) return;
// Construct a new termsize from COLUMNS and LINES, then set it in our data.
termsize_t new_termsize{
var_to_int_or(vars.get(L"COLUMNS", ENV_GLOBAL), termsize_t::DEFAULT_WIDTH),
var_to_int_or(vars.get(L"LINES", ENV_GLOBAL), termsize_t::DEFAULT_HEIGHT),
};
// Store our termsize as an environment override.
data_.acquire()->mark_override_from_env(new_termsize);
}
// static
void termsize_container_t::handle_winch() { s_tty_termsize_gen_count += 1; }
// static
void termsize_container_t::invalidate_tty() { s_tty_termsize_gen_count += 1; }

View file

@ -4,117 +4,10 @@
#ifndef FISH_TERMSIZE_H
#define FISH_TERMSIZE_H
#include <stdint.h>
#include "common.h"
#include "global_safety.h"
#include "maybe.h"
class environment_t;
class parser_t;
struct termsize_tester_t;
/// A simple value type wrapping up a terminal size.
struct termsize_t {
/// Default width and height.
static constexpr int DEFAULT_WIDTH = 80;
static constexpr int DEFAULT_HEIGHT = 24;
/// width of the terminal, in columns.
int width{DEFAULT_WIDTH};
/// height of the terminal, in rows.
int height{DEFAULT_HEIGHT};
/// Construct from width and height.
termsize_t(int w, int h) : width(w), height(h) {}
/// Return a default-sized termsize.
static termsize_t defaults() { return termsize_t{DEFAULT_WIDTH, DEFAULT_HEIGHT}; }
bool operator==(termsize_t rhs) const {
return this->width == rhs.width && this->height == rhs.height;
}
bool operator!=(termsize_t rhs) const { return !(*this == rhs); }
};
/// Termsize monitoring is more complicated than one may think.
/// The main source of complexity is the interaction between the environment variables COLUMNS/ROWS,
/// the WINCH signal, and the TIOCGWINSZ ioctl.
/// Our policy is "last seen wins": if COLUMNS or LINES is modified, we respect that until we get a
/// SIGWINCH.
struct termsize_container_t {
/// \return the termsize without applying any updates.
/// Return the default termsize if none.
termsize_t last() const;
/// If our termsize is stale, update it, using \p parser firing any events that may be
/// registered for COLUMNS and LINES.
/// \return the updated termsize.
termsize_t updating(parser_t &parser);
/// Initialize our termsize, using the given environment stack.
/// This will prefer to use COLUMNS and LINES, but will fall back to the tty size reader.
/// This does not change any variables in the environment.
termsize_t initialize(const environment_t &vars);
/// Note that a WINCH signal is received.
/// Naturally this may be called from within a signal handler.
static void handle_winch();
/// Invalidate the tty in the sense that we need to re-fetch its termsize.
static void invalidate_tty();
/// Note that COLUMNS and/or LINES global variables changed.
void handle_columns_lines_var_change(const environment_t &vars);
/// \return the singleton shared container.
static termsize_container_t &shared();
/// autocxx junk.
static termsize_t ffi_updating(parser_t &parser);
private:
/// A function used for accessing the termsize from the tty. This is only exposed for testing.
using tty_size_reader_func_t = maybe_t<termsize_t> (*)();
struct data_t {
// The last termsize returned by TIOCGWINSZ, or none if none.
maybe_t<termsize_t> last_from_tty{};
// The last termsize seen from the environment (COLUMNS/LINES), or none if none.
maybe_t<termsize_t> last_from_env{};
// The last-seen tty-invalidation generation count.
// Set to a huge value so it's initially stale.
uint32_t last_tty_gen_count{UINT32_MAX};
/// \return the current termsize from this data.
termsize_t current() const;
/// Mark that our termsize is (for the time being) from the environment, not the tty.
void mark_override_from_env(termsize_t ts);
};
// Construct from a reader function.
explicit termsize_container_t(tty_size_reader_func_t func) : tty_size_reader_(func) {}
// Update COLUMNS and LINES in the parser's stack.
void set_columns_lines_vars(termsize_t val, parser_t &parser);
// Our lock-protected data.
mutable owning_lock<data_t> data_{};
// An indication that we are currently in the process of setting COLUMNS and LINES, and so do
// not react to any changes.
relaxed_atomic_bool_t setting_env_vars_{false};
const tty_size_reader_func_t tty_size_reader_;
friend termsize_tester_t;
};
/// Convenience helper to return the last known termsize.
inline termsize_t termsize_last() { return termsize_container_t::shared().last(); }
#if INCLUDE_RUST_HEADERS
#include "termsize.rs.h"
#else
struct termsize_t;
#endif
#endif // FISH_TERMSIZE_H