To: vim_dev@googlegroups.com Subject: Patch 8.2.0669 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0669 Problem: MS-Windows: display in VTP is a bit slow. Solution: Optimize the code. (Nobuhiro Takasaki, closes #6014) Files: src/os_win32.c, src/screen.c *** ../vim-8.2.0668/src/os_win32.c 2020-04-28 20:44:38.872258441 +0200 --- src/os_win32.c 2020-04-30 20:55:23.813526617 +0200 *************** *** 5579,5590 **** COORD coord, DWORD n) { - DWORD dwDummy; - - FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy); - if (!USE_VTP) ! FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy); else { set_console_color_rgb(); --- 5579,5592 ---- COORD coord, DWORD n) { if (!USE_VTP) ! { ! DWORD dwDummy; ! ! FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy); ! FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, ! &dwDummy); ! } else { set_console_color_rgb(); *************** *** 6036,6058 **** { COORD coord = g_coord; DWORD written; ! DWORD n, cchwritten, cells; static WCHAR *unicodebuf = NULL; static int unibuflen = 0; ! int length; int cp = enc_utf8 ? CP_UTF8 : enc_codepage; ! ! length = MultiByteToWideChar(cp, 0, (LPCSTR)pchBuf, cbToWrite, 0, 0); ! if (unicodebuf == NULL || length > unibuflen) { ! vim_free(unicodebuf); ! unicodebuf = LALLOC_MULT(WCHAR, length); ! unibuflen = length; } - MultiByteToWideChar(cp, 0, (LPCSTR)pchBuf, cbToWrite, - unicodebuf, unibuflen); - - cells = mb_string2cells(pchBuf, cbToWrite); if (!USE_VTP) { --- 6038,6094 ---- { COORD coord = g_coord; DWORD written; ! DWORD n, cchwritten; ! static DWORD cells; static WCHAR *unicodebuf = NULL; static int unibuflen = 0; ! 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; ! ! if (cbToWrite != 1 || *pchBuf != ' ' || !enc_utf8) ! { ! 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; ! } ! } } if (!USE_VTP) { *************** *** 6060,6073 **** 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, coord, &cchwritten) == 0 || cchwritten == 0 || cchwritten == (DWORD)-1) cchwritten = 1; } else { ! if (WriteConsoleW(g_hConOut, unicodebuf, length, &cchwritten, NULL) == 0 || cchwritten == 0) cchwritten = 1; } --- 6096,6109 ---- 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, *utf8usingbuf, length, coord, &cchwritten) == 0 || cchwritten == 0 || cchwritten == (DWORD)-1) cchwritten = 1; } else { ! if (WriteConsoleW(g_hConOut, *utf8usingbuf, length, &cchwritten, NULL) == 0 || cchwritten == 0) cchwritten = 1; } *************** *** 6093,6103 **** g_coord.Y++; } ! gotoxy(g_coord.X + 1, g_coord.Y + 1); return written; } /* * mch_write(): write the output buffer to the screen, translating ESC --- 6129,6247 ---- g_coord.Y++; } ! // 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)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,6132 **** // 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"); if (p_wd) { --- 6268,6288 ---- // translate ESC | sequences into faked bios calls while (len--) { ! 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,6236 **** # endif char_u *p; int arg1 = 0, arg2 = 0, argc = 0, args[16]; 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 > s + len) break; arg1 = args[0]; arg2 = args[1]; if (*p == 'm') --- 6369,6420 ---- # 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': ! if (*(p = get_seq(args, &argc, s)) != 'm') ! goto notsgr; ! p = s; ! ! // Handling frequent optional sequences. Output to the screen ! // takes too long, so do not output as much as possible. ! ! // If resetFG,FG,BG,,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,7416 **** char_u buf[100]; va_list list; DWORD result; va_start(list, format); ! vim_vsnprintf((char *)buf, 100, (char *)format, list); va_end(list); ! WriteConsoleA(g_hConOut, buf, (DWORD)STRLEN(buf), &result, NULL); return (int)result; } --- 7590,7601 ---- char_u buf[100]; va_list list; DWORD result; + int len; va_start(list, format); ! len = vim_vsnprintf((char *)buf, 100, (char *)format, list); va_end(list); ! WriteConsoleA(g_hConOut, buf, (DWORD)len, &result, NULL); return (int)result; } *************** *** 7424,7453 **** vtp_sgr_bulks(1, args); } static void vtp_sgr_bulks( int argc, ! int *args ! ) { ! // 2('\033[') + 4('255.') * 16 + NUL ! char_u buf[2 + (4 * 16) + 1]; ! char_u *p; ! int i; ! p = buf; ! *p++ = '\033'; ! *p++ = '['; ! for (i = 0; i < argc; ++i) { ! p += vim_snprintf((char *)p, 4, "%d", args[i] & 0xff); ! *p++ = ';'; } - p--; - *p++ = 'm'; - *p = NUL; - vtp_printf((char *)buf); } # ifdef FEAT_TERMGUICOLORS --- 7609,7758 ---- 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) { ! #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; ! if (argc == 0) ! { ! sgrfgr = sgrbgr = -1; ! vtp_printf("033[m"); ! return; ! } ! in = out = 0; ! while (in < argc) { ! 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; } } # ifdef FEAT_TERMGUICOLORS *** ../vim-8.2.0668/src/screen.c 2020-04-24 22:18:57.167585301 +0200 --- src/screen.c 2020-04-30 20:55:23.813526617 +0200 *************** *** 1880,1885 **** --- 1880,1888 ---- screen_stop_highlight(void) { int do_ME = FALSE; // output T_ME code + #if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS) + int do_ME_fg, do_ME_bg; + #endif if (screen_attr != 0 #ifdef MSWIN *************** *** 1913,1928 **** --- 1916,1957 ---- #ifdef FEAT_TERMGUICOLORS p_tgc && aep->ae_u.cterm.fg_rgb != CTERMCOLOR ? aep->ae_u.cterm.fg_rgb != INVALCOLOR + # ifdef FEAT_VTP + ? !(do_ME_fg = TRUE) : (do_ME_fg = FALSE) + # endif : #endif aep->ae_u.cterm.fg_color) || ( #ifdef FEAT_TERMGUICOLORS p_tgc && aep->ae_u.cterm.bg_rgb != CTERMCOLOR ? aep->ae_u.cterm.bg_rgb != INVALCOLOR + # ifdef FEAT_VTP + ? !(do_ME_bg = TRUE) : (do_ME_bg = FALSE) + # endif : #endif aep->ae_u.cterm.bg_color))) do_ME = TRUE; + #if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS) + if (use_vtp()) + { + if (do_ME_fg && do_ME_bg) + do_ME = TRUE; + + // FG and BG cannot be separated in T_ME, which is not + // efficient. + if (!do_ME && do_ME_fg) + out_str((char_u *)"\033|39m"); // restore FG + if (!do_ME && do_ME_bg) + out_str((char_u *)"\033|49m"); // restore BG + } + else + { + // Process FG and BG at once. + if (!do_ME) + do_ME = do_ME_fg | do_ME_bg; + } + #endif } else { *** ../vim-8.2.0668/src/version.c 2020-04-30 20:21:36.028020847 +0200 --- src/version.c 2020-04-30 20:56:34.073287954 +0200 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 669, /**/ -- Due knot trussed yore spell chequer two fined awl miss steaks. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///