summaryrefslogtreecommitdiff
path: root/src/os_win32.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-04-30 20:59:57 +0200
committerBram Moolenaar <Bram@vim.org>2020-04-30 20:59:57 +0200
commit4e5534fab798ab7c95554da3bc80b08336aedc2b (patch)
treef27bfc8cb99b4f5f572653cc87e51c32191e4864 /src/os_win32.c
parent7f6f56f43c48f0b7288a4edd12b59fa7335f01d3 (diff)
downloadvim-git-4e5534fab798ab7c95554da3bc80b08336aedc2b.tar.gz
patch 8.2.0669: MS-Windows: display in VTP is a bit slowv8.2.0669
Problem: MS-Windows: display in VTP is a bit slow. Solution: Optimize the code. (Nobuhiro Takasaki, closes #6014)
Diffstat (limited to 'src/os_win32.c')
-rw-r--r--src/os_win32.c405
1 files changed, 355 insertions, 50 deletions
diff --git a/src/os_win32.c b/src/os_win32.c
index e4d8135ca..52bc95de8 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -5579,12 +5579,14 @@ clear_chars(
COORD coord,
DWORD n)
{
- DWORD dwDummy;
-
- FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
-
if (!USE_VTP)
- FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy);
+ {
+ DWORD dwDummy;
+
+ FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy);
+ FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord,
+ &dwDummy);
+ }
else
{
set_console_color_rgb();
@@ -6036,23 +6038,57 @@ write_chars(
{
COORD coord = g_coord;
DWORD written;
- DWORD n, cchwritten, cells;
+ DWORD n, cchwritten;
+ static DWORD cells;
static WCHAR *unicodebuf = NULL;
static int unibuflen = 0;
- int length;
+ static int length;
int cp = enc_utf8 ? CP_UTF8 : enc_codepage;
+ static WCHAR *utf8spbuf = NULL;
+ static int utf8splength;
+ static DWORD utf8spcells;
+ static WCHAR **utf8usingbuf = &unicodebuf;
- length = MultiByteToWideChar(cp, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0);
- if (unicodebuf == NULL || length > unibuflen)
+ if (cbToWrite != 1 || *pchBuf != ' ' || !enc_utf8)
{
- vim_free(unicodebuf);
- unicodebuf = LALLOC_MULT(WCHAR, length);
- unibuflen = length;
+ utf8usingbuf = &unicodebuf;
+ do
+ {
+ length = MultiByteToWideChar(cp, 0, (LPCSTR)pchBuf, cbToWrite,
+ unicodebuf, unibuflen);
+ if (length && length <= unibuflen)
+ break;
+ vim_free(unicodebuf);
+ unicodebuf = length ? LALLOC_MULT(WCHAR, length) : NULL;
+ unibuflen = unibuflen ? 0 : length;
+ } while(1);
+ cells = mb_string2cells(pchBuf, cbToWrite);
+ }
+ else // cbToWrite == 1 && *pchBuf == ' ' && enc_utf8
+ {
+ if (utf8usingbuf != &utf8spbuf)
+ {
+ if (utf8spbuf == NULL)
+ {
+ cells = mb_string2cells((char_u *)" ", 1);
+ length = MultiByteToWideChar(CP_UTF8, 0, " ", 1, NULL, 0);
+ utf8spbuf = LALLOC_MULT(WCHAR, length);
+ if (utf8spbuf != NULL)
+ {
+ MultiByteToWideChar(CP_UTF8, 0, " ", 1, utf8spbuf, length);
+ utf8usingbuf = &utf8spbuf;
+ utf8splength = length;
+ utf8spcells = cells;
+ }
+ }
+ else
+ {
+ utf8usingbuf = &utf8spbuf;
+ length = utf8splength;
+ cells = utf8spcells;
+ }
+ }
}
- MultiByteToWideChar(cp, 0, (LPCSTR)pchBuf, cbToWrite,
- unicodebuf, unibuflen);
-
- cells = mb_string2cells(pchBuf, cbToWrite);
if (!USE_VTP)
{
@@ -6060,14 +6096,14 @@ write_chars(
coord, &written);
// When writing fails or didn't write a single character, pretend one
// character was written, otherwise we get stuck.
- if (WriteConsoleOutputCharacterW(g_hConOut, unicodebuf, length,
+ if (WriteConsoleOutputCharacterW(g_hConOut, *utf8usingbuf, length,
coord, &cchwritten) == 0
|| cchwritten == 0 || cchwritten == (DWORD)-1)
cchwritten = 1;
}
else
{
- if (WriteConsoleW(g_hConOut, unicodebuf, length, &cchwritten,
+ if (WriteConsoleW(g_hConOut, *utf8usingbuf, length, &cchwritten,
NULL) == 0 || cchwritten == 0)
cchwritten = 1;
}
@@ -6093,11 +6129,119 @@ write_chars(
g_coord.Y++;
}
- gotoxy(g_coord.X + 1, g_coord.Y + 1);
+ // Cursor under VTP is always in the correct position, no need to reset.
+ if (!USE_VTP)
+ gotoxy(g_coord.X + 1, g_coord.Y + 1);
return written;
}
+ static char_u *
+get_seq(
+ int *args,
+ int *count,
+ char_u *head)
+{
+ int argc;
+ char_u *p;
+
+ if (head == NULL || *head != '\033')
+ return NULL;
+
+ argc = 0;
+ p = head;
+ ++p;
+ do
+ {
+ ++p;
+ args[argc] = getdigits(&p);
+ argc += (argc < 15) ? 1 : 0;
+ } while (*p == ';');
+ *count = argc;
+
+ return p;
+}
+
+ static char_u *
+get_sgr(
+ int *args,
+ int *count,
+ char_u *head)
+{
+ char_u *p = get_seq(args, count, head);
+
+ return (p && *p == 'm') ? ++p : NULL;
+}
+
+/*
+ * Pointer to next if SGR (^[[n;2;*;*;*m), NULL otherwise.
+ */
+ static char_u *
+sgrn2(
+ char_u *head,
+ int n)
+{
+ int argc;
+ int args[16];
+ char_u *p = get_sgr(args, &argc, head);
+
+ return p && argc == 5 && args[0] == n && args[1] == 2 ? p : NULL;
+}
+
+/*
+ * Pointer to next if SGR(^[[nm)<space>ESC, NULL otherwise.
+ */
+ static char_u *
+sgrnc(
+ char_u *head,
+ int n)
+{
+ int argc;
+ int args[16];
+ char_u *p = get_sgr(args, &argc, head);
+
+ return p && argc == 1 && args[0] == n && (p = skipwhite(p)) && *p == '\033'
+ ? p : NULL;
+}
+
+ static char_u *
+skipblank(char_u *q)
+{
+ char_u *p = q;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
+ ++p;
+ return p;
+}
+
+/*
+ * Pointer to the next if any whitespace that may follow SGR is ESC, otherwise
+ * NULL.
+ */
+ static char_u *
+sgrn2c(
+ char_u *head,
+ int n)
+{
+ char_u *p = sgrn2(head, n);
+
+ return p && *p != NUL && (p = skipblank(p)) && *p == '\033' ? p : NULL;
+}
+
+/*
+ * If there is only a newline between the sequence immediately following it,
+ * a pointer to the character following the newline is returned.
+ * Otherwise NULL.
+ */
+ static char_u *
+sgrn2cn(
+ char_u *head,
+ int n)
+{
+ char_u *p = sgrn2(head, n);
+
+ return p && p[0] == 0x0a && p[1] == '\033' ? ++p : NULL;
+}
/*
* mch_write(): write the output buffer to the screen, translating ESC
@@ -6124,9 +6268,21 @@ mch_write(
// translate ESC | sequences into faked bios calls
while (len--)
{
- // optimization: use one single write_chars for runs of text,
- // rather than once per character It ain't curses, but it helps.
- DWORD prefix = (DWORD)strcspn((char *)s, "\n\r\b\a\033");
+ int prefix = -1;
+ char_u ch;
+
+ // While processing a sequence, on rare occasions it seems that another
+ // sequence may be inserted asynchronously.
+ if (len < 0)
+ {
+ redraw_all_later(CLEAR);
+ return;
+ }
+
+ while((ch = s[++prefix]))
+ if (ch <= 0x1e && !(ch != '\n' && ch != '\r' && ch != '\b'
+ && ch != '\a' && ch != '\033'))
+ break;
if (p_wd)
{
@@ -6213,24 +6369,52 @@ mch_write(
# endif
char_u *p;
int arg1 = 0, arg2 = 0, argc = 0, args[16];
+ char_u *sp;
switch (s[2])
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- p = s + 1;
- do
- {
- ++p;
- args[argc] = getdigits(&p);
- argc += (argc < 15) ? 1 : 0;
- if (p > s + len)
- break;
- } while (*p == ';');
+ if (*(p = get_seq(args, &argc, s)) != 'm')
+ goto notsgr;
+
+ p = s;
- if (p > s + len)
+ // Handling frequent optional sequences. Output to the screen
+ // takes too long, so do not output as much as possible.
+
+ // If resetFG,FG,BG,<cr>,BG,FG are connected, the preceding
+ // resetFG,FG,BG are omitted.
+ if (sgrn2(sgrn2(sgrn2cn(sgrn2(sgrnc(p, 39), 38), 48), 48), 38))
+ {
+ p = sgrn2(sgrn2(sgrnc(p, 39), 38), 48);
+ len = len + 1 - (int)(p - s);
+ s = p;
break;
+ }
+
+ // If FG,BG,BG,FG of SGR are connected, the first FG can be
+ // omitted.
+ if (sgrn2(sgrn2(sgrn2c((sp = sgrn2(p, 38)), 48), 48), 38))
+ p = sp;
+
+ // If FG,BG,FG,BG of SGR are connected, the first FG can be
+ // omitted.
+ if (sgrn2(sgrn2(sgrn2c((sp = sgrn2(p, 38)), 48), 38), 48))
+ p = sp;
+ // If BG,BG of SGR are connected, the first BG can be omitted.
+ if (sgrn2((sp = sgrn2(p, 48)), 48))
+ p = sp;
+
+ // If restoreFG and FG are connected, the restoreFG can be
+ // omitted.
+ if (sgrn2((sp = sgrnc(p, 39)), 38))
+ p = sp;
+
+ p = get_seq(args, &argc, p);
+
+notsgr:
arg1 = args[0];
arg2 = args[1];
if (*p == 'm')
@@ -7406,11 +7590,12 @@ vtp_printf(
char_u buf[100];
va_list list;
DWORD result;
+ int len;
va_start(list, format);
- vim_vsnprintf((char *)buf, 100, (char *)format, list);
+ len = vim_vsnprintf((char *)buf, 100, (char *)format, list);
va_end(list);
- WriteConsoleA(g_hConOut, buf, (DWORD)STRLEN(buf), &result, NULL);
+ WriteConsoleA(g_hConOut, buf, (DWORD)len, &result, NULL);
return (int)result;
}
@@ -7424,30 +7609,150 @@ vtp_sgr_bulk(
vtp_sgr_bulks(1, args);
}
+#define FAST256(x) \
+ if ((*p-- = "0123456789"[(n = x % 10)]) \
+ && x >= 10 && (*p-- = "0123456789"[((m = x % 100) - n) / 10]) \
+ && x >= 100 && (*p-- = "012"[((x & 0xff) - m) / 100]));
+
+#define FAST256CASE(x) \
+ case x: \
+ FAST256(newargs[x - 1]);
+
static void
vtp_sgr_bulks(
int argc,
- int *args
-)
+ int *args)
{
- // 2('\033[') + 4('255.') * 16 + NUL
- char_u buf[2 + (4 * 16) + 1];
- char_u *p;
- int i;
+#define MAXSGR 16
+#define SGRBUFSIZE 2 + 4 * MAXSGR + 1 // '\033[' + SGR + 'm'
+ char_u buf[SGRBUFSIZE];
+ char_u *p;
+ int in, out;
+ int newargs[16];
+ static int sgrfgr = -1, sgrfgg, sgrfgb;
+ static int sgrbgr = -1, sgrbgg, sgrbgb;
- p = buf;
- *p++ = '\033';
- *p++ = '[';
+ if (argc == 0)
+ {
+ sgrfgr = sgrbgr = -1;
+ vtp_printf("033[m");
+ return;
+ }
- for (i = 0; i < argc; ++i)
+ in = out = 0;
+ while (in < argc)
{
- p += vim_snprintf((char *)p, 4, "%d", args[i] & 0xff);
- *p++ = ';';
+ int s = args[in];
+ int copylen = 1;
+
+ if (s == 38)
+ {
+ if (argc - in >= 5 && args[in + 1] == 2)
+ {
+ if (sgrfgr == args[in + 2] && sgrfgg == args[in + 3]
+ && sgrfgb == args[in + 4])
+ {
+ in += 5;
+ copylen = 0;
+ }
+ else
+ {
+ sgrfgr = args[in + 2];
+ sgrfgg = args[in + 3];
+ sgrfgb = args[in + 4];
+ copylen = 5;
+ }
+ }
+ else if (argc - in >= 3 && args[in + 1] == 5)
+ {
+ sgrfgr = -1;
+ copylen = 3;
+ }
+ }
+ else if (s == 48)
+ {
+ if (argc - in >= 5 && args[in + 1] == 2)
+ {
+ if (sgrbgr == args[in + 2] && sgrbgg == args[in + 3]
+ && sgrbgb == args[in + 4])
+ {
+ in += 5;
+ copylen = 0;
+ }
+ else
+ {
+ sgrbgr = args[in + 2];
+ sgrbgg = args[in + 3];
+ sgrbgb = args[in + 4];
+ copylen = 5;
+ }
+ }
+ else if (argc - in >= 3 && args[in + 1] == 5)
+ {
+ sgrbgr = -1;
+ copylen = 3;
+ }
+ }
+ else if (30 <= s && s <= 39)
+ sgrfgr = -1;
+ else if (90 <= s && s <= 97)
+ sgrfgr = -1;
+ else if (40 <= s && s <= 49)
+ sgrbgr = -1;
+ else if (100 <= s && s <= 107)
+ sgrbgr = -1;
+ else if (s == 0)
+ sgrfgr = sgrbgr = -1;
+
+ while (copylen--)
+ newargs[out++] = args[in++];
+ }
+
+ p = &buf[sizeof(buf) - 1];
+ *p-- = 'm';
+
+ switch (out)
+ {
+ int n, m;
+ DWORD r;
+
+ FAST256CASE(16);
+ *p-- = ';';
+ FAST256CASE(15);
+ *p-- = ';';
+ FAST256CASE(14);
+ *p-- = ';';
+ FAST256CASE(13);
+ *p-- = ';';
+ FAST256CASE(12);
+ *p-- = ';';
+ FAST256CASE(11);
+ *p-- = ';';
+ FAST256CASE(10);
+ *p-- = ';';
+ FAST256CASE(9);
+ *p-- = ';';
+ FAST256CASE(8);
+ *p-- = ';';
+ FAST256CASE(7);
+ *p-- = ';';
+ FAST256CASE(6);
+ *p-- = ';';
+ FAST256CASE(5);
+ *p-- = ';';
+ FAST256CASE(4);
+ *p-- = ';';
+ FAST256CASE(3);
+ *p-- = ';';
+ FAST256CASE(2);
+ *p-- = ';';
+ FAST256CASE(1);
+ *p-- = '[';
+ *p = '\033';
+ WriteConsoleA(g_hConOut, p, (DWORD)(&buf[SGRBUFSIZE] - p), &r, NULL);
+ default:
+ break;
}
- p--;
- *p++ = 'm';
- *p = NUL;
- vtp_printf((char *)buf);
}
# ifdef FEAT_TERMGUICOLORS