aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ui.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ui.c')
-rw-r--r--src/nvim/ui.c455
1 files changed, 440 insertions, 15 deletions
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index eab6251288..fb3325f163 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -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,27 +43,74 @@
#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)) {
- char_u *tofree = NULL;
+ if (silent_mode && !p_verbose) {
+ // Don't output anything in silent mode ("ex -s") unless 'verbose' set
+ return;
+ }
- if (output_conv.vc_type != CONV_NONE) {
- /* Convert characters from 'encoding' to 'termencoding'. */
- tofree = string_convert(&output_conv, s, &len);
- if (tofree != NULL)
- s = tofree;
- }
+ if (abstract_ui) {
+ parse_abstract_ui_codes(s, len);
+ return;
+ }
- term_write(s, len);
+ char_u *tofree = NULL;
- if (output_conv.vc_type != CONV_NONE)
- free(tofree);
+ if (output_conv.vc_type != CONV_NONE) {
+ /* Convert characters from 'encoding' to 'termencoding'. */
+ tofree = string_convert(&output_conv, s, &len);
+ if (tofree != NULL)
+ s = tofree;
}
+
+ term_write(s, len);
+
+ if (output_conv.vc_type != CONV_NONE)
+ free(tofree);
}
/*
@@ -69,7 +120,11 @@ void ui_write(char_u *s, int len)
*/
void ui_suspend(void)
{
- mch_suspend();
+ if (abstract_ui) {
+ UI_CALL(suspend);
+ } else {
+ mch_suspend();
+ }
}
/*
@@ -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)
{
- term_cursor_shape();
+ 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);
+}