Support piped input.
This commit is contained in:
@@ -58,12 +58,23 @@ struct IO {
|
|||||||
static volatile std::atomic_bool resized;
|
static volatile std::atomic_bool resized;
|
||||||
static void handle_sigwinch(int);
|
static void handle_sigwinch(int);
|
||||||
|
|
||||||
|
static enum struct Mode {
|
||||||
|
PIPE,
|
||||||
|
TERMINAL
|
||||||
|
} mode;
|
||||||
|
|
||||||
|
std::string pipe_input;
|
||||||
|
std::deque<char> input_queue;
|
||||||
|
|
||||||
IO();
|
IO();
|
||||||
~IO();
|
~IO();
|
||||||
|
|
||||||
IO(const IO &) = delete;
|
IO(const IO &) = delete;
|
||||||
IO &operator=(const IO &) = delete;
|
IO &operator=(const IO &) = delete;
|
||||||
|
|
||||||
|
bool interactive() const {
|
||||||
|
return mode == Mode::TERMINAL;
|
||||||
|
}
|
||||||
void enable_mouse();
|
void enable_mouse();
|
||||||
void disable_mouse();
|
void disable_mouse();
|
||||||
|
|
||||||
@@ -71,14 +82,14 @@ struct IO {
|
|||||||
std::pair<uint16_t, uint16_t> cursor_position();
|
std::pair<uint16_t, uint16_t> cursor_position();
|
||||||
void move_cursor(uint16_t row, uint16_t col);
|
void move_cursor(uint16_t row, uint16_t col);
|
||||||
|
|
||||||
|
std::pair<std::string, bool> read_pipe();
|
||||||
|
|
||||||
KeyEvent read_key();
|
KeyEvent read_key();
|
||||||
void write(const char *, uint64_t);
|
void write(const char *, uint64_t);
|
||||||
void write(std::string_view);
|
void write(std::string_view);
|
||||||
void write_line(std::string_view);
|
void write_line(std::string_view);
|
||||||
void run_pty(const std::string &);
|
void run_pty(const std::string &);
|
||||||
|
|
||||||
std::deque<char> input_queue;
|
|
||||||
|
|
||||||
KeyEvent::ReadResult get_next_byte(char &out);
|
KeyEvent::ReadResult get_next_byte(char &out);
|
||||||
void enqueue_bytes(const std::string &bytes);
|
void enqueue_bytes(const std::string &bytes);
|
||||||
static int utf8_seq_len(uint8_t byte);
|
static int utf8_seq_len(uint8_t byte);
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ struct CommandIO {
|
|||||||
|
|
||||||
CommandIO(BEd &);
|
CommandIO(BEd &);
|
||||||
std::pair<std::string, bool> run();
|
std::pair<std::string, bool> run();
|
||||||
|
std::pair<std::string, bool> run_pipe();
|
||||||
|
std::pair<std::string, bool> run_terminal();
|
||||||
void redraw();
|
void redraw();
|
||||||
};
|
};
|
||||||
} // namespace bed::internal::ui
|
} // namespace bed::internal::ui
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ struct TextMode {
|
|||||||
|
|
||||||
TextMode(BEd &);
|
TextMode(BEd &);
|
||||||
std::pair<vase::Shard *, bool> run();
|
std::pair<vase::Shard *, bool> run();
|
||||||
|
std::pair<vase::Shard *, bool> run_pipe();
|
||||||
|
std::pair<vase::Shard *, bool> run_terminal();
|
||||||
void grow();
|
void grow();
|
||||||
void redraw();
|
void redraw();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,31 @@
|
|||||||
#include "internal/io/io.h"
|
#include "internal/io/io.h"
|
||||||
|
|
||||||
namespace bed::internal::io {
|
namespace bed::internal::io {
|
||||||
|
std::pair<std::string, bool> IO::read_pipe() {
|
||||||
|
char buf[4096];
|
||||||
|
while (true) {
|
||||||
|
auto pos = pipe_input.find('\n');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
std::string line = pipe_input.substr(0, pos);
|
||||||
|
pipe_input.erase(0, pos + 1);
|
||||||
|
return {line, false};
|
||||||
|
}
|
||||||
|
ssize_t n = read(STDIN_FILENO, buf, sizeof(buf));
|
||||||
|
if (n > 0) {
|
||||||
|
pipe_input.append(buf, n);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
std::string line = std::move(pipe_input);
|
||||||
|
pipe_input.clear();
|
||||||
|
return {line, true};
|
||||||
|
}
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
throw fatal_error("Can't read stdin.", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KeyEvent::ReadResult IO::get_next_byte(char &out) {
|
KeyEvent::ReadResult IO::get_next_byte(char &out) {
|
||||||
if (!input_queue.empty()) {
|
if (!input_queue.empty()) {
|
||||||
out = input_queue.front();
|
out = input_queue.front();
|
||||||
|
|||||||
+18
-2
@@ -5,8 +5,12 @@ termios IO::orig_termios{};
|
|||||||
termios IO::raw_termios{};
|
termios IO::raw_termios{};
|
||||||
bool IO::cleaned = true;
|
bool IO::cleaned = true;
|
||||||
volatile std::atomic_bool IO::resized(false);
|
volatile std::atomic_bool IO::resized(false);
|
||||||
|
IO::Mode IO::mode = IO::Mode::PIPE;
|
||||||
|
|
||||||
IO::IO() {
|
IO::IO() {
|
||||||
|
if (!isatty(STDIN_FILENO))
|
||||||
|
return;
|
||||||
|
mode = Mode::TERMINAL;
|
||||||
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
|
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
|
||||||
throw fatal_error("Can't get terminal state.", 1);
|
throw fatal_error("Can't get terminal state.", 1);
|
||||||
struct sigaction sa{};
|
struct sigaction sa{};
|
||||||
@@ -30,16 +34,22 @@ IO::~IO() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IO::enable_mouse() {
|
void IO::enable_mouse() {
|
||||||
|
if (mode == Mode::PIPE)
|
||||||
|
throw fatal_error("no mouse in pipe mode.", 1);
|
||||||
const char *seq = "\x1b[?1000h";
|
const char *seq = "\x1b[?1000h";
|
||||||
write_all(STDOUT_FILENO, seq, 8);
|
write_all(STDOUT_FILENO, seq, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IO::disable_mouse() {
|
void IO::disable_mouse() {
|
||||||
|
if (mode == Mode::PIPE)
|
||||||
|
throw fatal_error("no mouse in pipe mode.", 1);
|
||||||
const char *seq = "\x1b[?1000l";
|
const char *seq = "\x1b[?1000l";
|
||||||
write_all(STDOUT_FILENO, seq, 8);
|
write_all(STDOUT_FILENO, seq, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<uint16_t, uint16_t> IO::terminal_size() {
|
std::pair<uint16_t, uint16_t> IO::terminal_size() {
|
||||||
|
if (mode == Mode::PIPE)
|
||||||
|
throw fatal_error("no terminal size in pipe mode.", 1);
|
||||||
struct winsize ws{};
|
struct winsize ws{};
|
||||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
|
||||||
throw fatal_error("Can't get terminal size.", 1);
|
throw fatal_error("Can't get terminal size.", 1);
|
||||||
@@ -47,6 +57,8 @@ std::pair<uint16_t, uint16_t> IO::terminal_size() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<uint16_t, uint16_t> IO::cursor_position() {
|
std::pair<uint16_t, uint16_t> IO::cursor_position() {
|
||||||
|
if (mode == Mode::PIPE)
|
||||||
|
throw fatal_error("no cursor in pipe mode.", 1);
|
||||||
write_all(STDOUT_FILENO, "\x1b[6n", 4);
|
write_all(STDOUT_FILENO, "\x1b[6n", 4);
|
||||||
std::string response;
|
std::string response;
|
||||||
char c;
|
char c;
|
||||||
@@ -69,7 +81,7 @@ std::pair<uint16_t, uint16_t> IO::cursor_position() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IO::enable_raw() {
|
void IO::enable_raw() {
|
||||||
if (!cleaned)
|
if (!cleaned || mode == Mode::PIPE)
|
||||||
return;
|
return;
|
||||||
std::string os = "\x1b[?2004h";
|
std::string os = "\x1b[?2004h";
|
||||||
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
||||||
@@ -79,7 +91,7 @@ void IO::enable_raw() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IO::cleanup() {
|
void IO::cleanup() {
|
||||||
if (cleaned)
|
if (cleaned || mode == Mode::PIPE)
|
||||||
return;
|
return;
|
||||||
std::string os = "\x1b[?1000l\x1b[?2004l";
|
std::string os = "\x1b[?1000l\x1b[?2004l";
|
||||||
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
||||||
@@ -93,6 +105,8 @@ void IO::handle_sigwinch(int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IO::move_cursor(uint16_t row, uint16_t col) {
|
void IO::move_cursor(uint16_t row, uint16_t col) {
|
||||||
|
if (mode == Mode::PIPE)
|
||||||
|
return;
|
||||||
char buf[32];
|
char buf[32];
|
||||||
int n = snprintf(buf, sizeof(buf), "\x1b[%u;%uH", row, col);
|
int n = snprintf(buf, sizeof(buf), "\x1b[%u;%uH", row, col);
|
||||||
write_all(STDOUT_FILENO, buf, n);
|
write_all(STDOUT_FILENO, buf, n);
|
||||||
@@ -112,6 +126,8 @@ void IO::write_line(std::string_view s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IO::run_pty(const std::string &cmd) {
|
void IO::run_pty(const std::string &cmd) {
|
||||||
|
if (mode == Mode::PIPE)
|
||||||
|
throw ed_error("Shell running not allowed in pipe mode.");
|
||||||
int master_fd = -1;
|
int master_fd = -1;
|
||||||
struct winsize ws{};
|
struct winsize ws{};
|
||||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
|
||||||
|
|||||||
@@ -69,6 +69,17 @@ CommandIO::CommandIO(BEd &bed) : bed(bed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::string, bool> CommandIO::run() {
|
std::pair<std::string, bool> CommandIO::run() {
|
||||||
|
if (!bed.io.interactive())
|
||||||
|
return run_pipe();
|
||||||
|
return run_terminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, bool> CommandIO::run_pipe() {
|
||||||
|
bed.io.write(prompt);
|
||||||
|
return bed.io.read_pipe();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, bool> CommandIO::run_terminal() {
|
||||||
auto [row, col] = bed.io.cursor_position();
|
auto [row, col] = bed.io.cursor_position();
|
||||||
auto [rows, cols] = bed.io.terminal_size();
|
auto [rows, cols] = bed.io.terminal_size();
|
||||||
if (row > rows)
|
if (row > rows)
|
||||||
|
|||||||
@@ -7,6 +7,23 @@ TextMode::TextMode(BEd &bed) : bed(bed) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<vase::Shard *, bool> TextMode::run() {
|
std::pair<vase::Shard *, bool> TextMode::run() {
|
||||||
|
if (!bed.io.interactive())
|
||||||
|
return run_pipe();
|
||||||
|
return run_terminal();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<vase::Shard *, bool> TextMode::run_pipe() {
|
||||||
|
cmd.clear();
|
||||||
|
while (true) {
|
||||||
|
auto [str, eof] = bed.io.read_pipe();
|
||||||
|
if (str == "." || eof)
|
||||||
|
break;
|
||||||
|
cmd += str + "\n";
|
||||||
|
}
|
||||||
|
return {vase::Shard::from_string(cmd.data(), cmd.length(), true), false};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<vase::Shard *, bool> TextMode::run_terminal() {
|
||||||
auto [row, col] = bed.io.cursor_position();
|
auto [row, col] = bed.io.cursor_position();
|
||||||
auto [rows, cols] = bed.io.terminal_size();
|
auto [rows, cols] = bed.io.terminal_size();
|
||||||
if (row > rows)
|
if (row > rows)
|
||||||
|
|||||||
Reference in New Issue
Block a user