ui: Add abstract_ui termcap and split UI layer

This is how Nvim behaves when the "abstract_ui" termcap is activated:

- No data is written/read to stdout/stdin by default.
- Instead of sending data to stdout, ui_write will parse the termcap codes
  and invoke dispatch functions in the ui.c module.
- The dispatch functions will forward the calls to all attached UI
  instances(each UI instance is an implementation of the UI layer and is
  registered with ui_attach).
- Like with the "builtin_gui" termcap, "abstract_ui" does not contain any key
  sequences. Instead, vim key strings(<cr>, <esc>, etc) are parsed directly by
  input_enqueue and the translated strings are pushed to the input buffer.

With this new input model, its not possible to send mouse events yet. Thats
because mouse sequence parsing happens in term.c/check_termcodes which must
return early when "abstract_ui" is activated.
This commit is contained in:
Thiago de Arruda 2014-12-06 11:27:36 -03:00
parent 8b6cfff6a1
commit 07e569a25d
11 changed files with 615 additions and 81 deletions

View File

@ -1984,10 +1984,6 @@ void free_cmdline_buf(void)
*/
static void draw_cmdline(int start, int len)
{
if (embedded_mode) {
return;
}
int i;
if (cmdline_star > 0)

View File

@ -2513,6 +2513,13 @@ fix_input_buffer (
int script /* TRUE when reading from a script */
)
{
if (abstract_ui) {
// Should not escape K_SPECIAL/CSI while in embedded mode because vim key
// codes keys are processed in input.c/input_enqueue.
buf[len] = NUL;
return len;
}
int i;
char_u *p = buf;

View File

@ -1251,6 +1251,8 @@ EXTERN int curr_tmode INIT(= TMODE_COOK); /* contains current terminal mode */
// If a msgpack-rpc channel should be started over stdin/stdout
EXTERN bool embedded_mode INIT(= false);
// Using the "abstract_ui" termcap
EXTERN bool abstract_ui INIT(= false);
/// Used to track the status of external functions.
/// Currently only used for iconv().

View File

@ -265,13 +265,6 @@ int main(int argc, char **argv)
term_init();
TIME_MSG("shell init");
event_init();
if (!embedded_mode) {
// Print a warning if stdout is not a terminal.
check_tty(&params);
}
/* This message comes before term inits, but after setting "silent_mode"
* when the input is not a tty. */
if (GARGCOUNT > 1 && !silent_mode)
@ -283,6 +276,7 @@ int main(int argc, char **argv)
// initial screen size of 80x20
full_screen = true;
screen_resize(80, 20, false);
termcapinit((uint8_t *)"abstract_ui");
} else {
// set terminal name and get terminal capabilities (will set full_screen)
// Do some initialization of the screen
@ -292,6 +286,16 @@ int main(int argc, char **argv)
TIME_MSG("Termcap init");
}
event_init();
if (abstract_ui) {
t_colors = 256;
} else {
// Print a warning if stdout is not a terminal TODO(tarruda): Remove this
// check once the new terminal UI is implemented
check_tty(&params);
}
/*
* Set the default values for the options that use Rows and Columns.
*/
@ -424,7 +428,6 @@ int main(int argc, char **argv)
TIME_MSG("waiting for return");
}
if (!embedded_mode) {
starttermcap(); // start termcap if not done by wait_return()
TIME_MSG("start termcap");
may_req_ambiguous_char_width();
@ -435,7 +438,6 @@ int main(int argc, char **argv)
}
scroll_start(); // may scroll the screen to the right position
}
/*
* Don't clear the screen when starting in Ex mode, unless using the GUI.

View File

@ -452,7 +452,7 @@ void setmouse(void)
return;
/* don't switch mouse on when not in raw mode (Ex mode) */
if (cur_tmode != TMODE_RAW) {
if (!abstract_ui && cur_tmode != TMODE_RAW) {
mch_setmouse(false);
return;
}
@ -470,10 +470,11 @@ void setmouse(void)
else
checkfor = MOUSE_NORMAL; /* assume normal mode */
if (mouse_has(checkfor))
mch_setmouse(true);
else
mch_setmouse(false);
if (mouse_has(checkfor)) {
ui_mouse_on();
} else {
ui_mouse_off();
}
}
/*

View File

@ -46,7 +46,7 @@ void input_init(void)
{
input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN);
if (embedded_mode) {
if (abstract_ui) {
return;
}
@ -57,7 +57,7 @@ void input_init(void)
void input_teardown(void)
{
if (embedded_mode) {
if (abstract_ui) {
return;
}
@ -67,7 +67,7 @@ void input_teardown(void)
// Listen for input
void input_start(void)
{
if (embedded_mode) {
if (abstract_ui) {
return;
}
@ -77,7 +77,7 @@ void input_start(void)
// Stop listening for input
void input_stop(void)
{
if (embedded_mode) {
if (abstract_ui) {
return;
}
@ -180,7 +180,23 @@ void input_buffer_restore(String str)
size_t input_enqueue(String keys)
{
size_t rv = rbuffer_write(input_buffer, keys.data, keys.size);
char *ptr = keys.data, *end = ptr + keys.size;
while (rbuffer_available(input_buffer) >= 6 && ptr < end) {
int new_size = trans_special((char_u **)&ptr,
(char_u *)rbuffer_write_ptr(input_buffer),
false);
if (!new_size) {
// copy the character unmodified
*rbuffer_write_ptr(input_buffer) = *ptr++;
new_size = 1;
}
// TODO(tarruda): Don't produce past unclosed '<' characters, except if
// there's a lot of characters after the '<'
rbuffer_produced(input_buffer, (size_t)new_size);
}
size_t rv = (size_t)(ptr - keys.data);
process_interrupts();
return rv;
}
@ -255,7 +271,7 @@ static void read_cb(RStream *rstream, void *data, bool at_eof)
static void convert_input(void)
{
if (embedded_mode || !rbuffer_available(input_buffer)) {
if (abstract_ui || !rbuffer_available(input_buffer)) {
// No input buffer space
return;
}
@ -335,7 +351,7 @@ static bool input_ready(void)
return typebuf_was_filled || // API call filled typeahead
rbuffer_pending(input_buffer) > 0 || // Stdin input
event_has_deferred() || // Events must be processed
(!embedded_mode && eof); // Stdin closed
(!abstract_ui && eof); // Stdin closed
}
// Exit because of an input read error.

View File

@ -45,7 +45,7 @@ void signal_init(void)
uv_signal_start(&shup, signal_cb, SIGHUP);
uv_signal_start(&squit, signal_cb, SIGQUIT);
uv_signal_start(&sterm, signal_cb, SIGTERM);
if (!embedded_mode) {
if (!abstract_ui) {
// TODO(tarruda): There must be an API function for resizing window
uv_signal_start(&swinch, signal_cb, SIGWINCH);
}

View File

@ -5824,9 +5824,12 @@ static void screen_start_highlight(int attr)
attrentry_T *aep = NULL;
screen_attr = attr;
if (full_screen
) {
{
if (full_screen) {
if (abstract_ui) {
char buf[20];
sprintf(buf, "\033|%dh", attr);
OUT_STR(buf);
} else {
if (attr > HL_ALL) { /* special HL attr. */
if (t_colors > 1)
aep = syn_cterm_attr2entry(attr);
@ -5877,9 +5880,13 @@ void screen_stop_highlight(void)
{
int do_ME = FALSE; /* output T_ME code */
if (screen_attr != 0
) {
{
if (screen_attr != 0) {
if (abstract_ui) {
// Handled in ui.c
char buf[20];
sprintf(buf, "\033|%dH", screen_attr);
OUT_STR(buf);
} else {
if (screen_attr > HL_ALL) { /* special HL attr. */
attrentry_T *aep;
@ -6558,11 +6565,14 @@ static void screenclear2(void)
{
int i;
if (starting == NO_SCREEN || ScreenLines == NULL
)
if (starting == NO_SCREEN || ScreenLines == NULL) {
return;
}
if (!abstract_ui) {
screen_attr = -1; /* force setting the Normal colors */
}
screen_stop_highlight(); /* don't want highlighting here */
@ -8156,14 +8166,19 @@ void screen_resize(int width, int height, int mustset)
++busy;
if (mustset || (ui_get_shellsize() == FAIL && height != 0)) {
// TODO(tarruda): "mustset" is still used in the old tests, which don't use
// "abstract_ui" yet. This will change when a new TUI is merged.
if (abstract_ui || mustset || (ui_get_shellsize() == FAIL && height != 0)) {
Rows = height;
Columns = width;
}
check_shellsize();
if (abstract_ui) {
ui_resize(width, height);
} else {
mch_set_shellsize();
} else
check_shellsize();
}
/* The window layout used to be adjusted here, but it now happens in
* screenalloc() (also invoked from screenclear()). That is because the

View File

@ -161,6 +161,33 @@ static bool detected_8bit = false; // detected 8-bit terminal
static struct builtin_term builtin_termcaps[] =
{
// abstract UI pseudo termcap, based on vim's "builtin_gui" termcap
{(int)KS_NAME, "abstract_ui"},
{(int)KS_CE, "\033|$"},
{(int)KS_AL, "\033|i"},
{(int)KS_CAL, "\033|%p1%dI"},
{(int)KS_DL, "\033|d"},
{(int)KS_CDL, "\033|%p1%dD"},
{(int)KS_CS, "\033|%p1%d;%p2%dR"},
{(int)KS_CL, "\033|C"},
// attributes switched on with 'h', off with * 'H'
{(int)KS_ME, "\033|31H"}, // HL_ALL
{(int)KS_MR, "\033|1h"}, // HL_INVERSE
{(int)KS_MD, "\033|2h"}, // HL_BOLD
{(int)KS_SE, "\033|16H"}, // HL_STANDOUT
{(int)KS_SO, "\033|16h"}, // HL_STANDOUT
{(int)KS_UE, "\033|8H"}, // HL_UNDERLINE
{(int)KS_US, "\033|8h"}, // HL_UNDERLINE
{(int)KS_CZR, "\033|4H"}, // HL_ITALIC
{(int)KS_CZH, "\033|4h"}, // HL_ITALIC
{(int)KS_VB, "\033|f"},
{(int)KS_MS, "y"},
{(int)KS_UT, "y"},
{(int)KS_LE, "\b"}, // cursor-left = BS
{(int)KS_ND, "\014"}, // cursor-right = CTRL-L
{(int)KS_CM, "\033|%p1%d;%p2%dM"},
// there are no key sequences here, for "abstract_ui" vim key codes are
// parsed directly in input_enqueue()
#ifndef NO_BUILTIN_TCAPS
@ -1162,6 +1189,10 @@ int set_termname(char_u *term)
if (silent_mode)
return OK;
if (!STRCMP(term, "abstract_ui")) {
abstract_ui = true;
}
detected_8bit = false; // reset 8-bit detection
if (term_is_builtin(term)) {
@ -1829,18 +1860,6 @@ void termcapinit(char_u *name)
/// Write s[len] to the screen.
void term_write(char_u *s, size_t len)
{
if (embedded_mode) {
// TODO(tarruda): This is a temporary hack to stop Neovim from writing
// messages to stdout in embedded mode. In the future, embedded mode will
// be the only possibility(GUIs will always start neovim with a msgpack-rpc
// over stdio) and this function won't exist.
//
// The reason for this is because before Neovim fully migrates to a
// msgpack-rpc-driven architecture, we must have a fully functional
// UI working
return;
}
(void) fwrite(s, len, 1, stdout);
#ifdef UNIX
@ -2296,7 +2315,7 @@ void shell_resized_check(void)
*/
void settmode(int tmode)
{
if (embedded_mode) {
if (abstract_ui) {
return;
}
@ -2340,7 +2359,7 @@ void starttermcap(void)
out_flush();
termcap_active = TRUE;
screen_start(); /* don't know where cursor is now */
{
if (!abstract_ui) {
may_req_termresponse();
/* Immediately check for a response. If t_Co changes, we don't
* want to redraw with wrong colors first. */
@ -2356,7 +2375,7 @@ void stoptermcap(void)
screen_stop_highlight();
reset_cterm_colors();
if (termcap_active) {
{
if (!abstract_ui) {
/* May need to discard T_CRV or T_U7 response. */
if (crv_status == CRV_SENT || u7_status == U7_SENT) {
# ifdef UNIX
@ -2545,6 +2564,11 @@ static int cursor_is_off = FALSE;
*/
void cursor_on(void)
{
if (abstract_ui) {
ui_cursor_on();
return;
}
if (cursor_is_off) {
out_str(T_VE);
cursor_is_off = FALSE;
@ -2556,6 +2580,11 @@ void cursor_on(void)
*/
void cursor_off(void)
{
if (abstract_ui) {
ui_cursor_off();
return;
}
if (full_screen) {
if (!cursor_is_off)
out_str(T_VI); /* disable cursor */
@ -2852,6 +2881,11 @@ void set_mouse_topline(win_T *wp)
*/
int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen)
{
if (abstract_ui) {
// codes are parsed by input.c/input_enqueue
return 0;
}
char_u *tp;
char_u *p;
int slen = 0; /* init for GCC */
@ -3883,6 +3917,10 @@ int find_term_bykeys(char_u *src)
*/
static void gather_termleader(void)
{
if (abstract_ui) {
return;
}
int len = 0;
if (check_for_codes)

View File

@ -15,20 +15,24 @@
* 3. Input buffer stuff.
*/
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include "nvim/vim.h"
#include "nvim/ui.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/ex_cmds2.h"
#include "nvim/fold.h"
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/ascii.h"
#include "nvim/misc1.h"
#include "nvim/misc2.h"
#include "nvim/mbyte.h"
#include "nvim/garray.h"
#include "nvim/memory.h"
#include "nvim/move.h"
@ -39,13 +43,61 @@
#include "nvim/os/input.h"
#include "nvim/os/signal.h"
#include "nvim/screen.h"
#include "nvim/syntax.h"
#include "nvim/term.h"
#include "nvim/window.h"
void ui_write(char_u *s, int len)
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui.c.generated.h"
#endif
#define MAX_UI_COUNT 16
static UI *uis[MAX_UI_COUNT];
static size_t ui_count = 0;
static int row, col;
static struct {
int top, bot, left, right;
} sr;
static int current_highlight_mask = 0;
static HlAttrs current_attrs = {
false, false, false, false, false, false, -1, -1
};
static bool cursor_enabled = true;
static int height = INT_MAX, width = INT_MAX;
// This set of macros allow us to use UI_CALL to invoke any function on
// registered UI instances. The functions can have 0-5 arguments(configurable
// by SELECT_NTH)
//
// See http://stackoverflow.com/a/11172679 for a better explanation of how it
// works.
#define UI_CALL(...) \
do { \
for (size_t i = 0; i < ui_count; i++) { \
UI *ui = uis[i]; \
UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
} \
} while (0)
#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore)
#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
#define UI_CALL_MORE(method, ...) ui->method(ui, __VA_ARGS__)
#define UI_CALL_ZERO(method) ui->method(ui)
void ui_write(uint8_t *s, int len)
{
/* Don't output anything in silent mode ("ex -s") unless 'verbose' set */
if (!(silent_mode && p_verbose == 0)) {
if (silent_mode && !p_verbose) {
// Don't output anything in silent mode ("ex -s") unless 'verbose' set
return;
}
if (abstract_ui) {
parse_abstract_ui_codes(s, len);
return;
}
char_u *tofree = NULL;
if (output_conv.vc_type != CONV_NONE) {
@ -60,7 +112,6 @@ void ui_write(char_u *s, int len)
if (output_conv.vc_type != CONV_NONE)
free(tofree);
}
}
/*
* If the machine has job control, use it to suspend the program,
@ -69,8 +120,12 @@ void ui_write(char_u *s, int len)
*/
void ui_suspend(void)
{
if (abstract_ui) {
UI_CALL(suspend);
} else {
mch_suspend();
}
}
/*
* Try to get the current Vim shell size. Put the result in Rows and Columns.
@ -79,6 +134,10 @@ void ui_suspend(void)
*/
int ui_get_shellsize(void)
{
if (abstract_ui) {
return FAIL;
}
int retval;
retval = mch_get_shellsize();
@ -98,7 +157,373 @@ int ui_get_shellsize(void)
*/
void ui_cursor_shape(void)
{
if (abstract_ui) {
ui_change_mode();
} else {
term_cursor_shape();
conceal_check_cursur_line();
}
}
void ui_resize(int width, int height)
{
sr.top = 0;
sr.bot = height - 1;
sr.left = 0;
sr.right = width - 1;
UI_CALL(resize, width, height);
}
void ui_cursor_on(void)
{
if (!cursor_enabled) {
UI_CALL(cursor_on);
cursor_enabled = true;
}
}
void ui_cursor_off(void)
{
if (full_screen) {
if (cursor_enabled) {
UI_CALL(cursor_off);
}
cursor_enabled = false;
}
}
void ui_mouse_on(void)
{
if (abstract_ui) {
UI_CALL(mouse_on);
} else {
mch_setmouse(true);
}
}
void ui_mouse_off(void)
{
if (abstract_ui) {
UI_CALL(mouse_off);
} else {
mch_setmouse(false);
}
}
// Notify that the current mode has changed. Can be used to change cursor
// shape, for example.
void ui_change_mode(void)
{
static int showing_insert_mode = MAYBE;
if (!full_screen)
return;
if (State & INSERT) {
if (showing_insert_mode != TRUE) {
UI_CALL(insert_mode);
}
showing_insert_mode = TRUE;
} else {
if (showing_insert_mode != FALSE) {
UI_CALL(normal_mode);
}
showing_insert_mode = FALSE;
}
conceal_check_cursur_line();
}
void ui_attach(UI *ui)
{
if (ui_count == MAX_UI_COUNT) {
abort();
}
uis[ui_count++] = ui;
resized(ui);
}
void ui_detach(UI *ui)
{
size_t shift_index = MAX_UI_COUNT;
// Find the index that will be removed
for (size_t i = 0; i < ui_count; i++) {
if (uis[i] == ui) {
shift_index = i;
break;
}
}
if (shift_index == MAX_UI_COUNT) {
abort();
}
// Shift UIs at "shift_index"
while (shift_index < ui_count - 1) {
uis[shift_index] = uis[shift_index + 1];
shift_index++;
}
ui_count--;
if (ui->width == width || ui->height == height) {
// It is possible that the UI being detached had the smallest screen,
// so check for the new minimum dimensions
width = height = INT_MAX;
for (size_t i = 0; i < ui_count; i++) {
check_dimensions(uis[i]);
}
}
if (ui_count) {
screen_resize(width, height, true);
}
}
static void highlight_start(int mask)
{
if (mask > HL_ALL) {
// attribute code
current_highlight_mask = mask;
} else {
// attribute mask
current_highlight_mask |= mask;
}
if (!ui_count) {
return;
}
set_highlight_args(current_highlight_mask, &current_attrs);
UI_CALL(highlight_set, current_attrs);
}
static void highlight_stop(int mask)
{
if (mask > HL_ALL) {
// attribute code
current_highlight_mask = HL_NORMAL;
} else {
// attribute mask
current_highlight_mask &= ~mask;
}
set_highlight_args(current_highlight_mask, &current_attrs);
UI_CALL(highlight_set, current_attrs);
}
static void set_highlight_args(int mask, HlAttrs *attrs)
{
attrentry_T *aep = NULL;
attrs->foreground = -1;
attrs->background = -1;
if (mask > HL_ALL) {
aep = syn_cterm_attr2entry(mask);
mask = aep ? aep->ae_attr : 0;
}
attrs->bold = mask & HL_BOLD;
attrs->standout = mask & HL_STANDOUT;
attrs->underline = mask & HL_UNDERLINE;
attrs->undercurl = mask & HL_UNDERCURL;
attrs->italic = mask & HL_ITALIC;
attrs->reverse = mask & HL_INVERSE;
if (aep && aep->ae_u.cterm.fg_color
&& (cterm_normal_fg_color != aep->ae_u.cterm.fg_color)) {
attrs->foreground = aep->ae_u.cterm.fg_color - 1;
}
if (aep && aep->ae_u.cterm.bg_color
&& (cterm_normal_bg_color != aep->ae_u.cterm.bg_color)) {
attrs->background = aep->ae_u.cterm.bg_color - 1;
}
}
static void parse_abstract_ui_codes(uint8_t *ptr, int len)
{
int arg1 = 0, arg2 = 0;
uint8_t *end = ptr + len, *p, c;
bool update_cursor = false;
while (ptr < end) {
if (ptr < end - 1 && ptr[0] == ESC && ptr[1] == '|') {
p = ptr + 2;
assert(p != end);
if (VIM_ISDIGIT(*p)) {
arg1 = (int)getdigits(&p);
if (p >= end) {
break;
}
if (*p == ';') {
p++;
arg2 = (int)getdigits(&p);
if (p >= end)
break;
}
}
switch (*p) {
case 'C':
UI_CALL(clear);
break;
case 'M':
ui_cursor_goto(arg1, arg2);
break;
case 's':
update_cursor = true;
break;
case 'R':
if (arg1 < arg2) {
sr.top = arg1;
sr.bot = arg2;
UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right);
} else {
sr.top = arg2;
sr.bot = arg1;
UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right);
}
break;
case 'V':
if (arg1 < arg2) {
sr.left = arg1;
sr.right = arg2;
UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right);
} else {
sr.left = arg2;
sr.right = arg1;
UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right);
}
break;
case 'd':
UI_CALL(scroll, 1);
break;
case 'D':
UI_CALL(scroll, arg1);
break;
case 'i':
UI_CALL(scroll, -1);
break;
case 'I':
UI_CALL(scroll, -arg1);
break;
case '$':
UI_CALL(eol_clear);
break;
case 'h':
highlight_start(arg1);
break;
case 'H':
highlight_stop(arg1);
break;
case 'f':
UI_CALL(visual_bell);
break;
default:
// Skip the ESC
p = ptr + 1;
break;
}
ptr = ++p;
} else if ((c = *ptr) < 0x20) {
// Ctrl character
if (c == '\n') {
ui_linefeed();
} else if (c == '\r') {
ui_carriage_return();
} else if (c == '\b') {
ui_cursor_left();
} else if (c == Ctrl_L) { // cursor right
ui_cursor_right();
} else if (c == Ctrl_G) {
UI_CALL(bell);
}
ptr++;
} else {
p = ptr;
while (p < end && (*p >= 0x20)) {
size_t clen = (size_t)mb_ptr2len(p);
UI_CALL(put, p, (size_t)clen);
col++;
if (mb_ptr2cells(p) > 1) {
// double cell character, blank the next cell
UI_CALL(put, NULL, 0);
col++;
}
p += clen;
}
ptr = p;
}
}
if (update_cursor) {
ui_cursor_shape();
}
UI_CALL(flush);
}
static void resized(UI *ui)
{
check_dimensions(ui);
screen_resize(width, height, true);
}
static void check_dimensions(UI *ui)
{
// The internal screen dimensions are always the minimum required to fit on
// all connected screens
if (ui->width < width) {
width = ui->width;
}
if (ui->height < height) {
height = ui->height;
}
}
static void ui_linefeed(void)
{
int new_col = 0;
int new_row = row;
if (new_row < sr.bot) {
new_row++;
} else {
UI_CALL(scroll, 1);
}
ui_cursor_goto(new_row, new_col);
}
static void ui_carriage_return(void)
{
int new_col = 0;
ui_cursor_goto(row, new_col);
}
static void ui_cursor_left(void)
{
int new_col = col - 1;
assert(new_col >= 0);
ui_cursor_goto(row, new_col);
}
static void ui_cursor_right(void)
{
int new_col = col + 1;
assert(new_col < width);
ui_cursor_goto(row, new_col);
}
static void ui_cursor_goto(int new_row, int new_col)
{
if (new_row == row && new_col == col) {
return;
}
row = new_row;
col = new_col;
UI_CALL(cursor_goto, row, col);
}

View File

@ -1,7 +1,39 @@
#ifndef NVIM_UI_H
#define NVIM_UI_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
typedef struct {
bool bold, standout, underline, undercurl, italic, reverse;
int foreground, background;
} HlAttrs;
typedef struct ui_t UI;
struct ui_t {
int width, height;
void *data;
void (*resize)(UI *ui, int rows, int columns);
void (*clear)(UI *ui);
void (*eol_clear)(UI *ui);
void (*cursor_goto)(UI *ui, int row, int col);
void (*cursor_on)(UI *ui);
void (*cursor_off)(UI *ui);
void (*mouse_on)(UI *ui);
void (*mouse_off)(UI *ui);
void (*insert_mode)(UI *ui);
void (*normal_mode)(UI *ui);
void (*set_scroll_region)(UI *ui, int top, int bot, int left, int right);
void (*scroll)(UI *ui, int count);
void (*highlight_set)(UI *ui, HlAttrs attrs);
void (*put)(UI *ui, uint8_t *str, size_t len);
void (*bell)(UI *ui);
void (*visual_bell)(UI *ui);
void (*flush)(UI *ui);
void (*suspend)(UI *ui);
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ui.h.generated.h"