Use a BufferedWriter to output in the terminal ui instead of stdio
Clearer control of buffering and flushing
This commit is contained in:
parent
1728274803
commit
1b7616b3fc
13
src/file.hh
13
src/file.hh
|
@ -131,7 +131,7 @@ struct BufferedWriter
|
||||||
~BufferedWriter() noexcept(false)
|
~BufferedWriter() noexcept(false)
|
||||||
{
|
{
|
||||||
if (m_pos != 0 and m_exception_count == std::uncaught_exceptions())
|
if (m_pos != 0 and m_exception_count == std::uncaught_exceptions())
|
||||||
Kakoune::write(m_fd, {m_buffer, m_pos});
|
flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(StringView data)
|
void write(StringView data)
|
||||||
|
@ -143,14 +143,17 @@ struct BufferedWriter
|
||||||
memcpy(m_buffer + (int)m_pos, data.data(), (int)write_len);
|
memcpy(m_buffer + (int)m_pos, data.data(), (int)write_len);
|
||||||
m_pos += write_len;
|
m_pos += write_len;
|
||||||
if (m_pos == size)
|
if (m_pos == size)
|
||||||
{
|
flush();
|
||||||
Kakoune::write(m_fd, {m_buffer, size});
|
|
||||||
m_pos = 0;
|
|
||||||
}
|
|
||||||
data = data.substr(write_len);
|
data = data.substr(write_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
Kakoune::write(m_fd, {m_buffer, m_pos});
|
||||||
|
m_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr ByteCount size = buffer_size;
|
static constexpr ByteCount size = buffer_size;
|
||||||
int m_fd;
|
int m_fd;
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
|
@ -190,32 +189,44 @@ void TerminalUI::Window::draw(DisplayCoord pos,
|
||||||
lines[(int)pos.line].append({}, size.column - pos.column, default_face);
|
lines[(int)pos.line].append({}, size.column - pos.column, default_face);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalUI::Screen::set_face(const Face& face)
|
struct Writer : BufferedWriter<>
|
||||||
|
{
|
||||||
|
using Writer::BufferedWriter::BufferedWriter;
|
||||||
|
~Writer() noexcept(false) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
static void format_with(Writer& writer, StringView format, Args&&... args)
|
||||||
|
{
|
||||||
|
format_with([&](StringView s) { writer.write(s); }, format, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerminalUI::Screen::set_face(const Face& face, Writer& writer)
|
||||||
{
|
{
|
||||||
static constexpr int fg_table[]{ 39, 30, 31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97 };
|
static constexpr int fg_table[]{ 39, 30, 31, 32, 33, 34, 35, 36, 37, 90, 91, 92, 93, 94, 95, 96, 97 };
|
||||||
static constexpr int bg_table[]{ 49, 40, 41, 42, 43, 44, 45, 46, 47, 100, 101, 102, 103, 104, 105, 106, 107 };
|
static constexpr int bg_table[]{ 49, 40, 41, 42, 43, 44, 45, 46, 47, 100, 101, 102, 103, 104, 105, 106, 107 };
|
||||||
static constexpr int attr_table[]{ 0, 4, 7, 5, 1, 2, 3, 9 };
|
static constexpr int attr_table[]{ 0, 4, 7, 5, 1, 2, 3, 9 };
|
||||||
|
|
||||||
auto set_color = [](bool fg, const Color& color, bool join) {
|
auto set_color = [&](bool fg, const Color& color, bool join) {
|
||||||
if (join)
|
if (join)
|
||||||
fputs(";", stdout);
|
writer.write(";");
|
||||||
if (color.isRGB())
|
if (color.isRGB())
|
||||||
printf("%d;2;%d;%d;%d", fg ? 38 : 48, color.r, color.g, color.b);
|
format_with(writer, "{};2;{};{};{}", fg ? 38 : 48, color.r, color.g, color.b);
|
||||||
else
|
else
|
||||||
printf("%d", (fg ? fg_table : bg_table)[(int)(char)color.color]);
|
format_with(writer, "{}", (fg ? fg_table : bg_table)[(int)(char)color.color]);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (m_active_face == face)
|
if (m_active_face == face)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fputs("\033[", stdout);
|
writer.write("\033[");
|
||||||
bool join = false;
|
bool join = false;
|
||||||
if (face.attributes != m_active_face.attributes)
|
if (face.attributes != m_active_face.attributes)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < std::size(attr_table); ++i)
|
for (int i = 0; i < std::size(attr_table); ++i)
|
||||||
{
|
{
|
||||||
if (face.attributes & (Attribute)(1 << i))
|
if (face.attributes & (Attribute)(1 << i))
|
||||||
printf(";%d", attr_table[i]);
|
format_with(writer, ";{}", attr_table[i]);
|
||||||
}
|
}
|
||||||
m_active_face = Face{{}, {}, face.attributes};
|
m_active_face = Face{{}, {}, face.attributes};
|
||||||
join = true;
|
join = true;
|
||||||
|
@ -227,24 +238,24 @@ void TerminalUI::Screen::set_face(const Face& face)
|
||||||
}
|
}
|
||||||
if (m_active_face.bg != face.bg)
|
if (m_active_face.bg != face.bg)
|
||||||
set_color(false, face.bg, join);
|
set_color(false, face.bg, join);
|
||||||
fputs("m", stdout);
|
writer.write("m");
|
||||||
|
|
||||||
m_active_face = face;
|
m_active_face = face;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalUI::Screen::output(bool force, bool synchronized)
|
void TerminalUI::Screen::output(bool force, bool synchronized, Writer& writer)
|
||||||
{
|
{
|
||||||
if (lines.empty())
|
if (lines.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// iTerm2 "begin synchronized update" sequence
|
// iTerm2 "begin synchronized update" sequence
|
||||||
if (synchronized)
|
if (synchronized)
|
||||||
printf("\033P=1s\033\\");
|
writer.write("\033P=1s\033\\");
|
||||||
|
|
||||||
if (force)
|
if (force)
|
||||||
{
|
{
|
||||||
hashes.clear();
|
hashes.clear();
|
||||||
printf("\033[m");
|
writer.write("\033[m");
|
||||||
m_active_face = Face{};
|
m_active_face = Face{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +286,7 @@ void TerminalUI::Screen::output(bool force, bool synchronized)
|
||||||
line += change.keep;
|
line += change.keep;
|
||||||
if (int del = change.del - change.add; del > 0)
|
if (int del = change.del - change.add; del > 0)
|
||||||
{
|
{
|
||||||
printf("\033[%dH\033[%dM", line + 1, del);
|
format_with(writer, "\033[{}H\033[{}M", line + 1, del);
|
||||||
line -= del;
|
line -= del;
|
||||||
}
|
}
|
||||||
line += change.del;
|
line += change.del;
|
||||||
|
@ -288,9 +299,9 @@ void TerminalUI::Screen::output(bool force, bool synchronized)
|
||||||
for (int i = 0; i < change.add; ++i)
|
for (int i = 0; i < change.add; ++i)
|
||||||
{
|
{
|
||||||
if (int add = change.add - change.del; i == 0 and add > 0)
|
if (int add = change.add - change.del; i == 0 and add > 0)
|
||||||
printf("\033[%dH\033[%dL", line + 1, add);
|
format_with(writer, "\033[{}H\033[{}L", line + 1, add);
|
||||||
else
|
else
|
||||||
printf("\033[%dH", line + 1);
|
format_with(writer, "\033[{}H", line + 1);
|
||||||
|
|
||||||
ColumnCount pending_move = 0;
|
ColumnCount pending_move = 0;
|
||||||
for (auto& [text, skip, face] : lines[line++].atoms)
|
for (auto& [text, skip, face] : lines[line++].atoms)
|
||||||
|
@ -300,25 +311,25 @@ void TerminalUI::Screen::output(bool force, bool synchronized)
|
||||||
|
|
||||||
if (pending_move != 0)
|
if (pending_move != 0)
|
||||||
{
|
{
|
||||||
printf("\033[%dC", (int)pending_move);
|
format_with(writer, "\033[{}C", (int)pending_move);
|
||||||
pending_move = 0;
|
pending_move = 0;
|
||||||
}
|
}
|
||||||
set_face(face);
|
set_face(face, writer);
|
||||||
fputs(text.c_str(), stdout);
|
writer.write(text.c_str());
|
||||||
if (skip > 3 and face.attributes == Attribute{})
|
if (skip > 3 and face.attributes == Attribute{})
|
||||||
{
|
{
|
||||||
fputs("\033[K", stdout);
|
writer.write("\033[K");
|
||||||
pending_move = skip;
|
pending_move = skip;
|
||||||
}
|
}
|
||||||
else if (skip > 0)
|
else if (skip > 0)
|
||||||
fputs(String{' ', skip}.c_str(), stdout);
|
writer.write(String{' ', skip}.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// iTerm2 "end synchronized update" sequence
|
// iTerm2 "end synchronized update" sequence
|
||||||
if (synchronized)
|
if (synchronized)
|
||||||
printf("\033P=2s\033\\");
|
writer.write("\033P=2s\033\\");
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int TerminalUI::default_shift_function_key;
|
constexpr int TerminalUI::default_shift_function_key;
|
||||||
|
@ -463,10 +474,11 @@ void TerminalUI::redraw(bool force)
|
||||||
|
|
||||||
m_info.blit(m_screen);
|
m_info.blit(m_screen);
|
||||||
|
|
||||||
m_screen.output(force, m_synchronized);
|
Writer writer{STDOUT_FILENO};
|
||||||
|
m_screen.output(force, m_synchronized, writer);
|
||||||
|
|
||||||
auto set_cursor_pos = [](DisplayCoord c) {
|
auto set_cursor_pos = [&](DisplayCoord c) {
|
||||||
printf("\033[%d;%dH", (int)c.line + 1, (int)c.column + 1);
|
format_with(writer, "\033[{};{}H", (int)c.line + 1, (int)c.column + 1);
|
||||||
};
|
};
|
||||||
if (m_cursor.mode == CursorMode::Prompt)
|
if (m_cursor.mode == CursorMode::Prompt)
|
||||||
set_cursor_pos({m_status_on_top ? 0 : m_dimensions.line, m_cursor.coord.column});
|
set_cursor_pos({m_status_on_top ? 0 : m_dimensions.line, m_cursor.coord.column});
|
||||||
|
@ -540,22 +552,17 @@ void TerminalUI::draw_status(const DisplayLine& status_line,
|
||||||
|
|
||||||
if (m_set_title)
|
if (m_set_title)
|
||||||
{
|
{
|
||||||
|
Writer writer{STDOUT_FILENO};
|
||||||
constexpr char suffix[] = " - Kakoune\007";
|
constexpr char suffix[] = " - Kakoune\007";
|
||||||
char buf[4 + 511 + 2] = "\033]2;";
|
writer.write("\033]2;");
|
||||||
// Fill title escape sequence buffer, removing non ascii characters
|
// Fill title escape sequence buffer, removing non ascii characters
|
||||||
auto buf_it = &buf[4], buf_end = &buf[4 + 511 - (sizeof(suffix) - 2)];
|
|
||||||
for (auto& atom : mode_line)
|
for (auto& atom : mode_line)
|
||||||
{
|
{
|
||||||
const auto str = atom.content();
|
const auto str = atom.content();
|
||||||
for (auto it = str.begin(), end = str.end();
|
for (auto it = str.begin(), end = str.end(); it != end; utf8::to_next(it, end))
|
||||||
it != end and buf_it != buf_end; utf8::to_next(it, end))
|
writer.write((*it >= 0x20 and *it <= 0x7e) ? *it : '?');
|
||||||
*buf_it++ = (*it >= 0x20 and *it <= 0x7e) ? *it : '?';
|
|
||||||
}
|
}
|
||||||
for (auto c : suffix)
|
writer.write(suffix);
|
||||||
*buf_it++ = c;
|
|
||||||
|
|
||||||
fputs(buf, stdout);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
@ -1333,33 +1340,27 @@ void TerminalUI::set_resize_pending()
|
||||||
|
|
||||||
void TerminalUI::setup_terminal()
|
void TerminalUI::setup_terminal()
|
||||||
{
|
{
|
||||||
// enable alternative screen buffer
|
write(STDOUT_FILENO,
|
||||||
fputs("\033[?1049h", stdout);
|
"\033[?1049h" // enable alternative screen buffer
|
||||||
// enable focus notify
|
"\033[?1004h" // enable focus notify
|
||||||
fputs("\033[?1004h", stdout);
|
"\033[>4;1m" // request CSI u style key reporting
|
||||||
// request CSI u style key reporting
|
"\033[22t" // save the current window title
|
||||||
fputs("\033[>4;1m", stdout);
|
"\033[?25l" // hide cursor
|
||||||
// save the current window title
|
"\033=" // set application keypad mode, so the keypad keys send unique codes
|
||||||
fputs("\033[22t", stdout);
|
);
|
||||||
// hide cursor
|
|
||||||
fputs("\033[?25l", stdout);
|
|
||||||
// set application keypad mode, so the keypad keys send unique codes
|
|
||||||
fputs("\033=", stdout);
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalUI::restore_terminal()
|
void TerminalUI::restore_terminal()
|
||||||
{
|
{
|
||||||
fputs("\033>", stdout);
|
write(STDOUT_FILENO,
|
||||||
fputs("\033[?25h", stdout);
|
"\033>"
|
||||||
fputs("\033[23t", stdout);
|
"\033[?25h"
|
||||||
fputs("\033[>4;0m", stdout);
|
"\033[23t"
|
||||||
fputs("\033[?1004l", stdout);
|
"\033[>4;0m"
|
||||||
fputs("\033[?1049l", stdout);
|
"\033[?1004l"
|
||||||
|
"\033[?1049l"
|
||||||
// set the terminal output back to default colours and style
|
"\033[m" // set the terminal output back to default colours and style
|
||||||
fputs("\033[m", stdout);
|
);
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalUI::enable_mouse(bool enabled)
|
void TerminalUI::enable_mouse(bool enabled)
|
||||||
|
@ -1370,20 +1371,20 @@ void TerminalUI::enable_mouse(bool enabled)
|
||||||
m_mouse_enabled = enabled;
|
m_mouse_enabled = enabled;
|
||||||
if (enabled)
|
if (enabled)
|
||||||
{
|
{
|
||||||
// force SGR mode
|
write(STDOUT_FILENO,
|
||||||
fputs("\033[?1006h", stdout);
|
"\033[?1006h" // force SGR mode
|
||||||
// enable mouse
|
"\033[?1000h" // enable mouse
|
||||||
fputs("\033[?1000h", stdout);
|
"\033[?1002h" // force enable report mouse position
|
||||||
// force enable report mouse position
|
);
|
||||||
fputs("\033[?1002h", stdout);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fputs("\033[?1002l", stdout);
|
write(STDOUT_FILENO,
|
||||||
fputs("\033[?1000l", stdout);
|
"\033[?1002l"
|
||||||
fputs("\033[?1006l", stdout);
|
"\033[?1000l"
|
||||||
|
"\033[?1006l"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerminalUI::set_ui_options(const Options& options)
|
void TerminalUI::set_ui_options(const Options& options)
|
||||||
|
|
|
@ -17,6 +17,7 @@ namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
struct DisplayAtom;
|
struct DisplayAtom;
|
||||||
|
struct Writer;
|
||||||
|
|
||||||
class TerminalUI : public UserInterface, public Singleton<TerminalUI>
|
class TerminalUI : public UserInterface, public Singleton<TerminalUI>
|
||||||
{
|
{
|
||||||
|
@ -88,8 +89,8 @@ private:
|
||||||
|
|
||||||
struct Screen : Window
|
struct Screen : Window
|
||||||
{
|
{
|
||||||
void output(bool force, bool synchronized);
|
void output(bool force, bool synchronized, Writer& writer);
|
||||||
void set_face(const Face& face);
|
void set_face(const Face& face, Writer& writer);
|
||||||
|
|
||||||
Vector<size_t> hashes;
|
Vector<size_t> hashes;
|
||||||
Face m_active_face;
|
Face m_active_face;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user