aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/tui
diff options
context:
space:
mode:
authorThiago de Arruda <tpadilha84@gmail.com>2015-09-07 07:47:17 -0300
committerThiago de Arruda <tpadilha84@gmail.com>2015-09-07 07:47:17 -0300
commitbb46cc2c9ce9a36f19df5c29a403c1feb4dbdf88 (patch)
tree499d8bcf29973994fbcbdc0b19bd9eb0a67ce726 /src/nvim/tui
parentf39ac698241885137e77efa4edeee7be21dd8deb (diff)
parenteb001a4abd2fbc740547c127807b2fc8367cc187 (diff)
downloadrneovim-bb46cc2c9ce9a36f19df5c29a403c1feb4dbdf88.tar.gz
rneovim-bb46cc2c9ce9a36f19df5c29a403c1feb4dbdf88.tar.bz2
rneovim-bb46cc2c9ce9a36f19df5c29a403c1feb4dbdf88.zip
Merge PR #3246 'Run builtin TUI in a another thread'
Diffstat (limited to 'src/nvim/tui')
-rw-r--r--src/nvim/tui/input.c (renamed from src/nvim/tui/term_input.inl)146
-rw-r--r--src/nvim/tui/input.h23
-rw-r--r--src/nvim/tui/tui.c435
3 files changed, 324 insertions, 280 deletions
diff --git a/src/nvim/tui/term_input.inl b/src/nvim/tui/input.c
index c396557160..b680e885df 100644
--- a/src/nvim/tui/term_input.inl
+++ b/src/nvim/tui/input.c
@@ -1,21 +1,86 @@
-#include <termkey.h>
+#include "nvim/tui/input.h"
+#include "nvim/vim.h"
+#include "nvim/api/vim.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/misc2.h"
#include "nvim/os/os.h"
#include "nvim/os/input.h"
#include "nvim/event/rstream.h"
-#include "nvim/event/time.h"
#define PASTETOGGLE_KEY "<f37>"
-struct term_input {
- int in_fd;
- bool paste_enabled;
- TermKey *tk;
- TimeWatcher timer_handle;
- Stream read_stream;
-};
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "tui/input.c.generated.h"
+#endif
+
+void term_input_init(TermInput *input, Loop *loop)
+{
+ input->loop = loop;
+ input->paste_enabled = false;
+ input->in_fd = 0;
+
+ const char *term = os_getenv("TERM");
+ if (!term) {
+ term = ""; // termkey_new_abstract assumes non-null (#2745)
+ }
+ input->tk = termkey_new_abstract(term, 0);
+ int curflags = termkey_get_canonflags(input->tk);
+ termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS);
+ // setup input handle
+ rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff, input);
+ // initialize a timer handle for handling ESC with libtermkey
+ time_watcher_init(loop, &input->timer_handle, input);
+ // Set the pastetoggle option to a special key that will be sent when
+ // \e[20{0,1}~/ are received
+ Error err = ERROR_INIT;
+ vim_set_option(cstr_as_string("pastetoggle"),
+ STRING_OBJ(cstr_as_string(PASTETOGGLE_KEY)), &err);
+}
+
+void term_input_destroy(TermInput *input)
+{
+ time_watcher_close(&input->timer_handle, NULL);
+ stream_close(&input->read_stream, NULL);
+ termkey_destroy(input->tk);
+}
+
+void term_input_start(TermInput *input)
+{
+ rstream_start(&input->read_stream, read_cb);
+}
+
+void term_input_stop(TermInput *input)
+{
+ rstream_stop(&input->read_stream);
+ time_watcher_stop(&input->timer_handle);
+}
+
+void term_input_set_encoding(TermInput *input, char* enc)
+{
+ int enc_flag = strcmp(enc, "utf-8") == 0 ? TERMKEY_FLAG_UTF8
+ : TERMKEY_FLAG_RAW;
+ termkey_set_flags(input->tk, enc_flag);
+}
+
+static void input_enqueue_event(void **argv)
+{
+ char *buf = argv[0];
+ input_enqueue(cstr_as_string(buf));
+ xfree(buf);
+}
+
+static void input_done_event(void **argv)
+{
+ input_done();
+}
+
+static void enqueue_input(char *buf, size_t size)
+{
+ loop_schedule(&loop, event_create(1, input_enqueue_event, 1,
+ xstrndup(buf, size)));
+}
static void forward_simple_utf8(TermKeyKey *key)
{
@@ -33,7 +98,7 @@ static void forward_simple_utf8(TermKeyKey *key)
}
buf[len] = 0;
- input_enqueue((String){.data = buf, .size = len});
+ enqueue_input(buf, len);
}
static void forward_modified_utf8(TermKey *tk, TermKeyKey *key)
@@ -48,7 +113,7 @@ static void forward_modified_utf8(TermKey *tk, TermKeyKey *key)
len = termkey_strfkey(tk, buf, sizeof(buf), key, TERMKEY_FORMAT_VIM);
}
- input_enqueue((String){.data = buf, .size = len});
+ enqueue_input(buf, len);
}
static void forward_mouse_event(TermKey *tk, TermKeyKey *key)
@@ -99,7 +164,7 @@ static void forward_mouse_event(TermKey *tk, TermKeyKey *key)
}
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row);
- input_enqueue((String){.data = buf, .size = len});
+ enqueue_input(buf, len);
}
static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force)
@@ -175,16 +240,16 @@ static bool handle_bracketed_paste(TermInput *input)
int state = get_real_state();
if (state & NORMAL) {
// Enter insert mode
- input_enqueue(cstr_as_string("i"));
+ enqueue_input("i", 1);
} else if (state & VISUAL) {
// Remove the selected text and enter insert mode
- input_enqueue(cstr_as_string("c"));
+ enqueue_input("c", 1);
} else if (!(state & INSERT)) {
// Don't mess with the paste option
return true;
}
}
- input_enqueue(cstr_as_string(PASTETOGGLE_KEY));
+ enqueue_input(PASTETOGGLE_KEY, sizeof(PASTETOGGLE_KEY) - 1);
input->paste_enabled = enable;
return true;
}
@@ -227,9 +292,9 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,
// ls *.md | xargs nvim
input->in_fd = 2;
stream_close(&input->read_stream, NULL);
- queue_put(loop.fast_events, restart_reading, 1, input);
+ queue_put(input->loop->fast_events, restart_reading, 1, input);
} else {
- input_done();
+ loop_schedule(&loop, event_create(1, input_done_event, 0));
}
return;
}
@@ -275,51 +340,6 @@ static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data,
static void restart_reading(void **argv)
{
TermInput *input = argv[0];
- rstream_init_fd(&loop, &input->read_stream, input->in_fd, 0xfff, input);
+ rstream_init_fd(input->loop, &input->read_stream, input->in_fd, 0xfff, input);
rstream_start(&input->read_stream, read_cb);
}
-
-static TermInput *term_input_new(void)
-{
- TermInput *rv = xmalloc(sizeof(TermInput));
- rv->paste_enabled = false;
- rv->in_fd = 0;
-
- const char *term = os_getenv("TERM");
- if (!term) {
- term = ""; // termkey_new_abstract assumes non-null (#2745)
- }
- rv->tk = termkey_new_abstract(term, 0);
- int curflags = termkey_get_canonflags(rv->tk);
- termkey_set_canonflags(rv->tk, curflags | TERMKEY_CANON_DELBS);
- // setup input handle
- rstream_init_fd(&loop, &rv->read_stream, rv->in_fd, 0xfff, rv);
- rstream_start(&rv->read_stream, read_cb);
- // initialize a timer handle for handling ESC with libtermkey
- time_watcher_init(&loop, &rv->timer_handle, rv);
- // Set the pastetoggle option to a special key that will be sent when
- // \e[20{0,1}~/ are received
- Error err = ERROR_INIT;
- vim_set_option(cstr_as_string("pastetoggle"),
- STRING_OBJ(cstr_as_string(PASTETOGGLE_KEY)), &err);
- return rv;
-}
-
-static void term_input_destroy(TermInput *input)
-{
- time_watcher_stop(&input->timer_handle);
- time_watcher_close(&input->timer_handle, NULL);
- rstream_stop(&input->read_stream);
- stream_close(&input->read_stream, NULL);
- termkey_destroy(input->tk);
- // Run once to remove references to input/timer handles
- loop_poll_events(&loop, 0);
- xfree(input);
-}
-
-static void term_input_set_encoding(TermInput *input, char* enc)
-{
- int enc_flag = strcmp(enc, "utf-8") == 0 ? TERMKEY_FLAG_UTF8
- : TERMKEY_FLAG_RAW;
- termkey_set_flags(input->tk, enc_flag);
-}
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
new file mode 100644
index 0000000000..033f53b4e2
--- /dev/null
+++ b/src/nvim/tui/input.h
@@ -0,0 +1,23 @@
+#ifndef NVIM_TUI_INPUT_H
+#define NVIM_TUI_INPUT_H
+
+#include <stdbool.h>
+
+#include <termkey.h>
+#include "nvim/event/stream.h"
+#include "nvim/event/time.h"
+
+typedef struct term_input {
+ int in_fd;
+ bool paste_enabled;
+ TermKey *tk;
+ TimeWatcher timer_handle;
+ Loop *loop;
+ Stream read_stream;
+} TermInput;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "tui/input.h.generated.h"
+#endif
+
+#endif // NVIM_TUI_INPUT_H
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index acc2ccc682..b2bb80a092 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -17,46 +17,45 @@
#include "nvim/event/loop.h"
#include "nvim/event/signal.h"
#include "nvim/tui/tui.h"
+#include "nvim/tui/input.h"
+#include "nvim/os/input.h"
+#include "nvim/os/os.h"
#include "nvim/strings.h"
+#include "nvim/ugrid.h"
+#include "nvim/ui_bridge.h"
// Space reserved in the output buffer to restore the cursor to normal when
// flushing. No existing terminal will require 32 bytes to do that.
#define CNORM_COMMAND_MAX_SIZE 32
#define OUTBUF_SIZE 0xffff
-typedef struct term_input TermInput;
-
-#include "term_input.inl"
-
typedef struct {
int top, bot, left, right;
} Rect;
typedef struct {
- char data[7];
- HlAttrs attrs;
-} Cell;
-
-typedef struct {
+ UIBridgeData *bridge;
+ Loop *loop;
+ bool stop;
unibi_var_t params[9];
char buf[OUTBUF_SIZE];
size_t bufpos, bufsize;
- TermInput *input;
- uv_loop_t *write_loop;
+ TermInput input;
+ uv_loop_t write_loop;
unibi_term *ut;
uv_tty_t output_handle;
- SignalWatcher winch_handle;
- Rect scroll_region;
+ SignalWatcher winch_handle, cont_handle;
+ bool cont_received;
+ // Event scheduled by the ui bridge. Since the main thread suspends until
+ // the event is handled, it is fine to use a single field instead of a queue
+ Event scheduled_event;
+ UGrid grid;
kvec_t(Rect) invalid_regions;
- int row, col;
- int bg, fg;
int out_fd;
- int old_height;
bool can_use_terminal_scroll;
bool mouse_enabled;
bool busy;
- HlAttrs attrs, print_attrs;
- Cell **screen;
+ HlAttrs print_attrs;
int showing_mode;
struct {
int enable_mouse, disable_mouse;
@@ -72,32 +71,41 @@ static bool volatile got_winch = false;
# include "tui/tui.c.generated.h"
#endif
-#define EMPTY_ATTRS ((HlAttrs){false, false, false, false, false, -1, -1})
-
-#define FOREACH_CELL(ui, top, bot, left, right, go, code) \
- do { \
- TUIData *data = ui->data; \
- for (int row = top; row <= bot; ++row) { \
- Cell *cells = data->screen[row]; \
- if (go) { \
- unibi_goto(ui, row, left); \
- } \
- for (int col = left; col <= right; ++col) { \
- Cell *cell = cells + col; \
- (void)(cell); \
- code; \
- } \
- } \
- } while (0)
-
UI *tui_start(void)
{
- TUIData *data = xcalloc(1, sizeof(TUIData));
UI *ui = xcalloc(1, sizeof(UI));
- ui->data = data;
- data->attrs = data->print_attrs = EMPTY_ATTRS;
- data->fg = data->bg = -1;
+ ui->stop = tui_stop;
+ ui->rgb = os_getenv("NVIM_TUI_ENABLE_TRUE_COLOR") != NULL;
+ ui->resize = tui_resize;
+ ui->clear = tui_clear;
+ ui->eol_clear = tui_eol_clear;
+ ui->cursor_goto = tui_cursor_goto;
+ ui->update_menu = tui_update_menu;
+ ui->busy_start = tui_busy_start;
+ ui->busy_stop = tui_busy_stop;
+ ui->mouse_on = tui_mouse_on;
+ ui->mouse_off = tui_mouse_off;
+ ui->mode_change = tui_mode_change;
+ ui->set_scroll_region = tui_set_scroll_region;
+ ui->scroll = tui_scroll;
+ ui->highlight_set = tui_highlight_set;
+ ui->put = tui_put;
+ ui->bell = tui_bell;
+ ui->visual_bell = tui_visual_bell;
+ ui->update_fg = tui_update_fg;
+ ui->update_bg = tui_update_bg;
+ ui->flush = tui_flush;
+ ui->suspend = tui_suspend;
+ ui->set_title = tui_set_title;
+ ui->set_icon = tui_set_icon;
+ ui->set_encoding = tui_set_encoding;
+ return ui_bridge_attach(ui, tui_main, tui_scheduler);
+}
+
+static void terminfo_start(UI *ui)
+{
+ TUIData *data = ui->data;
data->can_use_terminal_scroll = true;
data->bufpos = 0;
data->bufsize = sizeof(data->buf) - CNORM_COMMAND_MAX_SIZE;
@@ -109,12 +117,8 @@ UI *tui_start(void)
data->unibi_ext.enter_insert_mode = -1;
data->unibi_ext.enter_replace_mode = -1;
data->unibi_ext.exit_insert_mode = -1;
-
// write output to stderr if stdout is not a tty
data->out_fd = os_isatty(1) ? 1 : (os_isatty(2) ? 2 : 1);
- kv_init(data->invalid_regions);
- // setup term input
- data->input = term_input_new();
// setup unibilium
data->ut = unibi_from_env();
if (!data->ut) {
@@ -128,62 +132,14 @@ UI *tui_start(void)
unibi_out(ui, unibi_clear_screen);
// Enable bracketed paste
unibi_out(ui, data->unibi_ext.enable_bracketed_paste);
- // setup output handle in a separate event loop(we wanna do synchronous
- // write to the tty)
- data->write_loop = xmalloc(sizeof(uv_loop_t));
- uv_loop_init(data->write_loop);
- uv_tty_init(data->write_loop, &data->output_handle, data->out_fd, 0);
+ uv_loop_init(&data->write_loop);
+ uv_tty_init(&data->write_loop, &data->output_handle, data->out_fd, 0);
uv_tty_set_mode(&data->output_handle, UV_TTY_MODE_RAW);
-
- // Obtain screen dimensions
- update_size(ui);
-
- // listen for SIGWINCH
- signal_watcher_init(&loop, &data->winch_handle, ui);
- data->winch_handle.events = queue_new_child(loop.events);
- signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
-
- ui->stop = tui_stop;
- ui->rgb = os_getenv("NVIM_TUI_ENABLE_TRUE_COLOR") != NULL;
- ui->data = data;
- ui->resize = tui_resize;
- ui->clear = tui_clear;
- ui->eol_clear = tui_eol_clear;
- ui->cursor_goto = tui_cursor_goto;
- ui->update_menu = tui_update_menu;
- ui->busy_start = tui_busy_start;
- ui->busy_stop = tui_busy_stop;
- ui->mouse_on = tui_mouse_on;
- ui->mouse_off = tui_mouse_off;
- ui->mode_change = tui_mode_change;
- ui->set_scroll_region = tui_set_scroll_region;
- ui->scroll = tui_scroll;
- ui->highlight_set = tui_highlight_set;
- ui->put = tui_put;
- ui->bell = tui_bell;
- ui->visual_bell = tui_visual_bell;
- ui->update_fg = tui_update_fg;
- ui->update_bg = tui_update_bg;
- ui->flush = tui_flush;
- ui->suspend = tui_suspend;
- ui->set_title = tui_set_title;
- ui->set_icon = tui_set_icon;
- ui->set_encoding = tui_set_encoding;
- // Attach
- ui_attach(ui);
- return ui;
}
-static void tui_stop(UI *ui)
+static void terminfo_stop(UI *ui)
{
TUIData *data = ui->data;
- // Destroy common stuff
- kv_destroy(data->invalid_regions);
- signal_watcher_stop(&data->winch_handle);
- queue_free(data->winch_handle.events);
- signal_watcher_close(&data->winch_handle, NULL);
- // Destroy input stuff
- term_input_destroy(data->input);
// Destroy output stuff
tui_mode_change(ui, NORMAL);
tui_mouse_off(ui);
@@ -196,24 +152,99 @@ static void tui_stop(UI *ui)
flush_buf(ui);
uv_tty_reset_mode();
uv_close((uv_handle_t *)&data->output_handle, NULL);
- uv_run(data->write_loop, UV_RUN_DEFAULT);
- if (uv_loop_close(data->write_loop)) {
+ uv_run(&data->write_loop, UV_RUN_DEFAULT);
+ if (uv_loop_close(&data->write_loop)) {
abort();
}
- xfree(data->write_loop);
unibi_destroy(data->ut);
- destroy_screen(data);
+}
+
+static void tui_terminal_start(UI *ui)
+{
+ TUIData *data = ui->data;
+ data->print_attrs = EMPTY_ATTRS;
+ ugrid_init(&data->grid);
+ terminfo_start(ui);
+ update_size(ui);
+ signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
+ term_input_start(&data->input);
+}
+
+static void tui_terminal_stop(UI *ui)
+{
+ TUIData *data = ui->data;
+ term_input_stop(&data->input);
+ signal_watcher_stop(&data->winch_handle);
+ terminfo_stop(ui);
+ ugrid_free(&data->grid);
+}
+
+static void tui_stop(UI *ui)
+{
+ tui_terminal_stop(ui);
+ TUIData *data = ui->data;
+ data->stop = true;
+}
+
+// Main function of the TUI thread
+static void tui_main(UIBridgeData *bridge, UI *ui)
+{
+ Loop tui_loop;
+ loop_init(&tui_loop, NULL);
+ TUIData *data = xcalloc(1, sizeof(TUIData));
+ ui->data = data;
+ data->bridge = bridge;
+ data->loop = &tui_loop;
+ kv_init(data->invalid_regions);
+ signal_watcher_init(data->loop, &data->winch_handle, ui);
+ signal_watcher_init(data->loop, &data->cont_handle, data);
+ signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT);
+ // initialize input reading structures
+ term_input_init(&data->input, &tui_loop);
+ tui_terminal_start(ui);
+ data->stop = false;
+ // allow the main thread to continue, we are ready to start handling UI
+ // callbacks
+ CONTINUE(bridge);
+
+ while (!data->stop) {
+ loop_poll_events(&tui_loop, -1);
+ }
+
+ term_input_destroy(&data->input);
+ signal_watcher_stop(&data->cont_handle);
+ signal_watcher_close(&data->cont_handle, NULL);
+ signal_watcher_close(&data->winch_handle, NULL);
+ loop_close(&tui_loop);
+ kv_destroy(data->invalid_regions);
xfree(data);
- ui_detach(ui);
xfree(ui);
}
+static void tui_scheduler(Event event, void *d)
+{
+ UI *ui = d;
+ TUIData *data = ui->data;
+ loop_schedule(data->loop, event);
+}
+
+static void refresh_event(void **argv)
+{
+ ui_refresh();
+}
+
+static void sigcont_cb(SignalWatcher *watcher, int signum, void *data)
+{
+ ((TUIData *)data)->cont_received = true;
+}
+
static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)
{
got_winch = true;
UI *ui = data;
update_size(ui);
- ui_refresh();
+ // run refresh_event in nvim main loop
+ loop_schedule(&loop, event_create(1, refresh_event, 0));
}
static bool attrs_differ(HlAttrs a1, HlAttrs a2)
@@ -234,9 +265,10 @@ static void update_attrs(UI *ui, HlAttrs attrs)
data->print_attrs = attrs;
unibi_out(ui, unibi_exit_attribute_mode);
+ UGrid *grid = &data->grid;
- int fg = attrs.foreground != -1 ? attrs.foreground : data->fg;
- int bg = attrs.background != -1 ? attrs.background : data->bg;
+ int fg = attrs.foreground != -1 ? attrs.foreground : grid->fg;
+ int bg = attrs.background != -1 ? attrs.background : grid->bg;
if (ui->rgb) {
if (fg != -1) {
@@ -278,25 +310,25 @@ static void update_attrs(UI *ui, HlAttrs attrs)
}
}
-static void print_cell(UI *ui, Cell *ptr)
+static void print_cell(UI *ui, UCell *ptr)
{
update_attrs(ui, ptr->attrs);
out(ui, ptr->data, strlen(ptr->data));
}
-static void clear_region(UI *ui, int top, int bot, int left, int right,
- bool refresh)
+static void clear_region(UI *ui, int top, int bot, int left, int right)
{
TUIData *data = ui->data;
- HlAttrs clear_attrs = EMPTY_ATTRS;
- clear_attrs.foreground = data->fg;
- clear_attrs.background = data->bg;
- update_attrs(ui, clear_attrs);
+ UGrid *grid = &data->grid;
bool cleared = false;
- if (refresh && data->bg == -1 && right == ui->width -1) {
+ if (grid->bg == -1 && right == ui->width -1) {
// Background is set to the default color and the right edge matches the
// screen end, try to use terminal codes for clearing the requested area.
+ HlAttrs clear_attrs = EMPTY_ATTRS;
+ clear_attrs.foreground = grid->fg;
+ clear_attrs.background = grid->bg;
+ update_attrs(ui, clear_attrs);
if (left == 0) {
if (bot == ui->height - 1) {
if (top == 0) {
@@ -319,36 +351,26 @@ static void clear_region(UI *ui, int top, int bot, int left, int right,
}
}
- bool clear = refresh && !cleared;
- FOREACH_CELL(ui, top, bot, left, right, clear, {
- cell->data[0] = ' ';
- cell->data[1] = 0;
- cell->attrs = clear_attrs;
- if (clear) {
+ if (!cleared) {
+ // could not clear using faster terminal codes, refresh the whole region
+ int currow = -1;
+ UGRID_FOREACH_CELL(grid, top, bot, left, right, {
+ if (currow != row) {
+ unibi_goto(ui, row, col);
+ currow = row;
+ }
print_cell(ui, cell);
- }
- });
+ });
+ }
// restore cursor
- unibi_goto(ui, data->row, data->col);
+ unibi_goto(ui, grid->row, grid->col);
}
static void tui_resize(UI *ui, int width, int height)
{
TUIData *data = ui->data;
- destroy_screen(data);
-
- data->screen = xmalloc((size_t)height * sizeof(Cell *));
- for (int i = 0; i < height; i++) {
- data->screen[i] = xcalloc((size_t)width, sizeof(Cell));
- }
-
- data->old_height = height;
- data->scroll_region.top = 0;
- data->scroll_region.bot = height - 1;
- data->scroll_region.left = 0;
- data->scroll_region.right = width - 1;
- data->row = data->col = 0;
+ ugrid_resize(&data->grid, width, height);
if (!got_winch) { // Try to resize the terminal window.
char r[16]; // enough for 9999x9999
@@ -362,22 +384,23 @@ static void tui_resize(UI *ui, int width, int height)
static void tui_clear(UI *ui)
{
TUIData *data = ui->data;
- clear_region(ui, data->scroll_region.top, data->scroll_region.bot,
- data->scroll_region.left, data->scroll_region.right, true);
+ UGrid *grid = &data->grid;
+ ugrid_clear(grid);
+ clear_region(ui, grid->top, grid->bot, grid->left, grid->right);
}
static void tui_eol_clear(UI *ui)
{
TUIData *data = ui->data;
- clear_region(ui, data->row, data->row, data->col,
- data->scroll_region.right, true);
+ UGrid *grid = &data->grid;
+ ugrid_eol_clear(grid);
+ clear_region(ui, grid->row, grid->row, grid->col, grid->right);
}
static void tui_cursor_goto(UI *ui, int row, int col)
{
TUIData *data = ui->data;
- data->row = row;
- data->col = col;
+ ugrid_goto(&data->grid, row, col);
unibi_goto(ui, row, col);
}
@@ -435,11 +458,7 @@ static void tui_set_scroll_region(UI *ui, int top, int bot, int left,
int right)
{
TUIData *data = ui->data;
- data->scroll_region.top = top;
- data->scroll_region.bot = bot;
- data->scroll_region.left = left;
- data->scroll_region.right = right;
-
+ ugrid_set_scroll_region(&data->grid, top, bot, left, right);
data->can_use_terminal_scroll =
left == 0 && right == ui->width - 1
&& ((top == 0 && bot == ui->height - 1)
@@ -449,31 +468,24 @@ static void tui_set_scroll_region(UI *ui, int top, int bot, int left,
static void tui_scroll(UI *ui, int count)
{
TUIData *data = ui->data;
- int top = data->scroll_region.top;
- int bot = data->scroll_region.bot;
- int left = data->scroll_region.left;
- int right = data->scroll_region.right;
+ UGrid *grid = &data->grid;
+ int clear_top, clear_bot;
+ ugrid_scroll(grid, count, &clear_top, &clear_bot);
if (data->can_use_terminal_scroll) {
// Change terminal scroll region and move cursor to the top
- data->params[0].i = top;
- data->params[1].i = bot;
+ data->params[0].i = grid->top;
+ data->params[1].i = grid->bot;
unibi_out(ui, unibi_change_scroll_region);
- unibi_goto(ui, top, left);
+ unibi_goto(ui, grid->top, grid->left);
// also set default color attributes or some terminals can become funny
HlAttrs clear_attrs = EMPTY_ATTRS;
- clear_attrs.foreground = data->fg;
- clear_attrs.background = data->bg;
+ clear_attrs.foreground = grid->fg;
+ clear_attrs.background = grid->bg;
update_attrs(ui, clear_attrs);
}
- // Compute start/stop/step for the loop below, also use terminal scroll
- // if possible
- int start, stop, step;
if (count > 0) {
- start = top;
- stop = bot - count + 1;
- step = 1;
if (data->can_use_terminal_scroll) {
if (count == 1) {
unibi_out(ui, unibi_delete_line);
@@ -484,9 +496,6 @@ static void tui_scroll(UI *ui, int count)
}
} else {
- start = bot;
- stop = top - count - 1;
- step = -1;
if (data->can_use_terminal_scroll) {
if (count == -1) {
unibi_out(ui, unibi_insert_line);
@@ -502,52 +511,30 @@ static void tui_scroll(UI *ui, int count)
data->params[0].i = 0;
data->params[1].i = ui->height - 1;
unibi_out(ui, unibi_change_scroll_region);
- unibi_goto(ui, data->row, data->col);
- }
-
- int i;
- // Scroll internal screen
- for (i = start; i != stop; i += step) {
- Cell *target_row = data->screen[i] + left;
- Cell *source_row = data->screen[i + count] + left;
- memcpy(target_row, source_row, sizeof(Cell) * (size_t)(right - left + 1));
- }
-
- // clear emptied region, updating the terminal if its builtin scrolling
- // facility was used. This is done when the background color is not the
- // default, since scrolling may leave wrong background in the cleared area.
- bool update_clear = data->bg != -1 && data->can_use_terminal_scroll;
- if (count > 0) {
- clear_region(ui, stop, stop + count - 1, left, right, update_clear);
+ unibi_goto(ui, grid->row, grid->col);
+
+ if (grid->bg != -1) {
+ // Update the cleared area of the terminal if its builtin scrolling
+ // facility was used and the background color is not the default. This is
+ // required because scrolling may leave wrong background in the cleared
+ // area.
+ clear_region(ui, clear_top, clear_bot, grid->left, grid->right);
+ }
} else {
- clear_region(ui, stop + count + 1, stop, left, right, update_clear);
- }
-
- if (!data->can_use_terminal_scroll) {
// Mark the entire scroll region as invalid for redrawing later
- invalidate(ui, data->scroll_region.top, data->scroll_region.bot,
- data->scroll_region.left, data->scroll_region.right);
+ invalidate(ui, grid->top, grid->bot, grid->left, grid->right);
}
}
static void tui_highlight_set(UI *ui, HlAttrs attrs)
{
- ((TUIData *)ui->data)->attrs = attrs;
+ ((TUIData *)ui->data)->grid.attrs = attrs;
}
static void tui_put(UI *ui, uint8_t *text, size_t size)
{
TUIData *data = ui->data;
- Cell *cell = data->screen[data->row] + data->col;
- cell->data[size] = 0;
- cell->attrs = data->attrs;
-
- if (text) {
- memcpy(cell->data, text, size);
- }
-
- print_cell(ui, cell);
- data->col += 1;
+ print_cell(ui, ugrid_put(&data->grid, text, size));
}
static void tui_bell(UI *ui)
@@ -562,40 +549,64 @@ static void tui_visual_bell(UI *ui)
static void tui_update_fg(UI *ui, int fg)
{
- ((TUIData *)ui->data)->fg = fg;
+ ((TUIData *)ui->data)->grid.fg = fg;
}
static void tui_update_bg(UI *ui, int bg)
{
- ((TUIData *)ui->data)->bg = bg;
+ ((TUIData *)ui->data)->grid.bg = bg;
}
static void tui_flush(UI *ui)
{
TUIData *data = ui->data;
+ UGrid *grid = &data->grid;
while (kv_size(data->invalid_regions)) {
Rect r = kv_pop(data->invalid_regions);
- FOREACH_CELL(ui, r.top, r.bot, r.left, r.right, true, {
+ int currow = -1;
+ UGRID_FOREACH_CELL(grid, r.top, r.bot, r.left, r.right, {
+ if (currow != row) {
+ unibi_goto(ui, row, col);
+ currow = row;
+ }
print_cell(ui, cell);
});
}
- unibi_goto(ui, data->row, data->col);
+ unibi_goto(ui, grid->row, grid->col);
flush_buf(ui);
}
-static void tui_suspend(UI *ui)
+static void suspend_event(void **argv)
{
+ UI *ui = argv[0];
TUIData *data = ui->data;
bool enable_mouse = data->mouse_enabled;
- tui_stop(ui);
+ tui_terminal_stop(ui);
+ data->cont_received = false;
kill(0, SIGTSTP);
- ui = tui_start();
+ while (!data->cont_received) {
+ // poll the event loop until SIGCONT is received
+ loop_poll_events(data->loop, -1);
+ }
+ tui_terminal_start(ui);
if (enable_mouse) {
tui_mouse_on(ui);
}
+ // resume the main thread
+ CONTINUE(data->bridge);
+}
+
+static void tui_suspend(UI *ui)
+{
+ TUIData *data = ui->data;
+ // kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT
+ // before continuing. This is done in another callback to avoid
+ // loop_poll_events recursion
+ queue_put_event(data->loop->fast_events,
+ event_create(1, suspend_event, 1, ui));
}
static void tui_set_title(UI *ui, char *title)
@@ -617,7 +628,7 @@ static void tui_set_icon(UI *ui, char *icon)
static void tui_set_encoding(UI *ui, char* enc)
{
TUIData *data = ui->data;
- term_input_set_encoding(data->input, enc);
+ term_input_set_encoding(&data->input, enc);
}
static void invalidate(UI *ui, int top, int bot, int left, int right)
@@ -698,8 +709,8 @@ end:
height = DFLT_ROWS;
}
- ui->width = width;
- ui->height = height;
+ data->bridge->bridge.width = ui->width = width;
+ data->bridge->bridge.height = ui->height = height;
}
static void unibi_goto(UI *ui, int row, int col)
@@ -882,7 +893,7 @@ static void flush_buf(UI *ui)
buf.base = data->buf;
buf.len = data->bufpos;
uv_write(&req, (uv_stream_t *)&data->output_handle, &buf, 1, NULL);
- uv_run(data->write_loop, UV_RUN_DEFAULT);
+ uv_run(&data->write_loop, UV_RUN_DEFAULT);
data->bufpos = 0;
if (!data->busy) {
@@ -891,13 +902,3 @@ static void flush_buf(UI *ui)
unibi_out(ui, unibi_cursor_invisible);
}
}
-
-static void destroy_screen(TUIData *data)
-{
- if (data->screen) {
- for (int i = 0; i < data->old_height; i++) {
- xfree(data->screen[i]);
- }
- xfree(data->screen);
- }
-}