# HG changeset patch # User Christian Brabandt # Date 1519330505 -3600 # Node ID 65c3e8259124e8c88bd21001e54f8326c5aa38ed # Parent 97fb19f366537413fcccebca6224046241fdb5b7 patch 8.0.1531: cannot use 24 bit colors in MS-Windows console commit https://github.com/vim/vim/commit/cafafb381a04e33f3ce9cd15dd9f94b73226831f Author: Bram Moolenaar Date: Thu Feb 22 21:07:09 2018 +0100 patch 8.0.1531: cannot use 24 bit colors in MS-Windows console Problem: Cannot use 24 bit colors in MS-Windows console. Solution: Add support for vcon. (Nobuhiro Takasaki, Ken Takasaki, fixes #1270, fixes #2060) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 8.0. Last change: 2018 Feb 03 +*options.txt* For Vim version 8.0. Last change: 2018 Feb 22 VIM REFERENCE MANUAL by Bram Moolenaar @@ -5203,8 +5203,11 @@ A jump table for the options with a shor {not in Vi} Maximum amount of memory (in Kbyte) to use for one buffer. When this limit is reached allocating extra memory for a buffer will cause - other memory to be freed. The maximum usable value is about 2000000. - Use this to work without a limit. Also see 'maxmemtot'. + other memory to be freed. + The maximum usable value is about 2000000. Use this to work without a + limit. + The value is ignored when 'swapfile' is off. + Also see 'maxmemtot'. *'maxmempattern'* *'mmp'* 'maxmempattern' 'mmp' number (default 1000) @@ -5233,6 +5236,8 @@ A jump table for the options with a shor need more than 2 Gbyte for text editing? Keep in mind that text is stored in the swap file, one can edit files > 2 Gbyte anyway. We do need the memory to store undo info. + Buffers with 'swapfile' off still count to the total amount of memory + used. Also see 'maxmem'. *'menuitems'* *'mis'* @@ -5956,14 +5961,13 @@ A jump table for the options with a shor |ins-completion-menu|. *'pumwidth'* *'pw'* -'pumwidth' 'pw' number (default 0) +'pumwidth' 'pw' number (default 15) global {not available when compiled without the |+insert_expand| feature} {not in Vi} Determines the minium width to use for the popup menu for Insert mode - completion. When zero the default of 15 screen cells is used. - |ins-completion-menu|. + completion. |ins-completion-menu|. *'pythondll'* 'pythondll' string (default depends on the build) @@ -7903,17 +7907,23 @@ A jump table for the options with a shor :set encoding=utf-8 < You need to do this when your system has no locale support for UTF-8. - *'termguicolors'* *'tgc'* + *'termguicolors'* *'tgc'* *E954* 'termguicolors' 'tgc' boolean (default off) global {not in Vi} {not available when compiled without the |+termguicolors| feature} When on, uses |highlight-guifg| and |highlight-guibg| attributes in - the terminal (thus using 24-bit color). Requires a ISO-8613-3 - compatible terminal. - If setting this option does not work (produces a colorless UI) - reading |xterm-true-color| might help. + the terminal (thus using 24-bit color). + + Requires a ISO-8613-3 compatible terminal. If setting this option + does not work (produces a colorless UI) reading |xterm-true-color| + might help. + + For Win32 console, Windows 10 version 1703 (Creators Update) or later + is required. Use this check to find out: > + if has('vcon') +< Note that the "cterm" attributes are still used, not the "gui" ones. NOTE: This option is reset when 'compatible' is set. diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -851,7 +851,7 @@ static struct fst #ifdef FEAT_TERMINAL {"term_dumpdiff", 2, 3, f_term_dumpdiff}, {"term_dumpload", 1, 2, f_term_dumpload}, - {"term_dumpwrite", 2, 4, f_term_dumpwrite}, + {"term_dumpwrite", 2, 3, f_term_dumpwrite}, {"term_getaltscreen", 1, 1, f_term_getaltscreen}, {"term_getattr", 2, 2, f_term_getattr}, {"term_getcursor", 1, 1, f_term_getcursor}, @@ -6323,9 +6323,9 @@ f_has(typval_T *argvars, typval_T *rettv else if (STRICMP(name, "syntax_items") == 0) n = syntax_present(curwin); #endif -#if defined(WIN3264) - else if (STRICMP(name, "win95") == 0) - n = FALSE; /* Win9x is no more supported. */ +#ifdef FEAT_VTP + else if (STRICMP(name, "vcon") == 0) + n = has_vtp_working(); #endif #ifdef FEAT_NETBEANS_INTG else if (STRICMP(name, "netbeans_enabled") == 0) diff --git a/src/feature.h b/src/feature.h --- a/src/feature.h +++ b/src/feature.h @@ -1394,3 +1394,10 @@ || (defined(WIN3264) && defined(FEAT_GUI_W32)) # define FEAT_FILTERPIPE #endif + +/* + * +vtp: Win32 virtual console. + */ +#if !defined(FEAT_GUI) && defined(WIN3264) +# define FEAT_VTP +#endif diff --git a/src/misc1.c b/src/misc1.c --- a/src/misc1.c +++ b/src/misc1.c @@ -3714,7 +3714,22 @@ vim_beep( && !(gui.in_use && gui.starting) #endif ) + { out_str_cf(T_VB); +#ifdef FEAT_VTP + /* No restore color information, refresh the screen. */ + if (has_vtp_working() != 0 +# ifdef FEAT_TERMGUICOLORS + && p_tgc +# endif + ) + { + redraw_later(CLEAR); + update_screen(0); + redrawcmd(); + } +#endif + } else out_char(BELL); #ifdef ELAPSED_FUNC diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -8690,10 +8690,25 @@ set_bool_option( /* 'termguicolors' */ else if ((int *)varp == &p_tgc) { +# ifdef FEAT_VTP + /* Do not turn on 'tgc' when 24-bit colors are not supported. */ + if (!has_vtp_working()) + { + p_tgc = 0; + return (char_u*)N_("E954: 24-bit colors are not supported on this environment"); + } + swap_tcap(); +# endif # ifdef FEAT_GUI if (!gui.in_use && !gui.starting) # endif highlight_gui_started(); +# ifdef FEAT_VTP + control_console_color_rgb(); + /* reset t_Co */ + if (STRCMP(T_NAME, "win32") == 0) + set_termname(T_NAME); +# endif } #endif diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -203,6 +203,32 @@ static int win32_setattrs(char_u *name, static int win32_set_archive(char_u *name); #ifndef FEAT_GUI_W32 +static int vtp_working = 0; +static void vtp_init(); +static void vtp_exit(); +static int vtp_printf(char *format, ...); +static void vtp_sgr_bulk(int arg); +static void vtp_sgr_bulks(int argc, int *argv); + +static guicolor_T save_console_bg_rgb; +static guicolor_T save_console_fg_rgb; + +# ifdef FEAT_TERMGUICOLORS +# define USE_VTP (vtp_working && p_tgc) +# else +# define USE_VTP 0 +# endif + +static void set_console_color_rgb(void); +static void reset_console_color_rgb(void); +#endif + +/* This flag is newly created from Windows 10 */ +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +#ifndef FEAT_GUI_W32 static int suppress_winsize = 1; /* don't fiddle with console */ #endif @@ -211,6 +237,54 @@ static char_u *exe_path = NULL; static BOOL win8_or_later = FALSE; #ifndef FEAT_GUI_W32 +/* Dynamic loading for portability */ +typedef struct _DYN_CONSOLE_SCREEN_BUFFER_INFOEX +{ + ULONG cbSize; + COORD dwSize; + COORD dwCursorPosition; + WORD wAttributes; + SMALL_RECT srWindow; + COORD dwMaximumWindowSize; + WORD wPopupAttributes; + BOOL bFullscreenSupported; + COLORREF ColorTable[16]; +} DYN_CONSOLE_SCREEN_BUFFER_INFOEX, *PDYN_CONSOLE_SCREEN_BUFFER_INFOEX; +typedef BOOL (WINAPI *PfnGetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX); +static PfnGetConsoleScreenBufferInfoEx pGetConsoleScreenBufferInfoEx; +typedef BOOL (WINAPI *PfnSetConsoleScreenBufferInfoEx)(HANDLE, PDYN_CONSOLE_SCREEN_BUFFER_INFOEX); +static PfnSetConsoleScreenBufferInfoEx pSetConsoleScreenBufferInfoEx; +static BOOL has_csbiex = FALSE; + +/* + * Get version number including build number + */ +typedef BOOL (WINAPI *PfnRtlGetVersion)(LPOSVERSIONINFOW); +# define MAKE_VER(major, minor, build) \ + (((major) << 24) | ((minor) << 16) | (build)) + + static DWORD +get_build_number(void) +{ + OSVERSIONINFOW osver = {sizeof(OSVERSIONINFOW)}; + HMODULE hNtdll; + PfnRtlGetVersion pRtlGetVersion; + DWORD ver = MAKE_VER(0, 0, 0); + + hNtdll = GetModuleHandle("ntdll.dll"); + if (hNtdll != NULL) + { + pRtlGetVersion = + (PfnRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion"); + pRtlGetVersion(&osver); + ver = MAKE_VER(min(osver.dwMajorVersion, 255), + min(osver.dwMinorVersion, 255), + min(osver.dwBuildNumber, 32767)); + } + return ver; +} + + /* * Version of ReadConsoleInput() that works with IME. * Works around problems on Windows 8. @@ -2537,6 +2611,7 @@ mch_init(void) /* set termcap codes to current text attributes */ update_tcap(g_attrCurrent); + swap_tcap(); GetConsoleCursorInfo(g_hConOut, &g_cci); GetConsoleMode(g_hConIn, &g_cmodein); @@ -2577,6 +2652,8 @@ mch_init(void) #ifdef FEAT_CLIPBOARD win_clip_init(); #endif + + vtp_init(); } /* @@ -2589,6 +2666,8 @@ mch_exit(int r) { exiting = TRUE; + vtp_exit(); + stoptermcap(); if (g_fWindInitCalled) settmode(TMODE_COOK); @@ -3801,7 +3880,15 @@ mch_settmode(int tmode) if (g_fMouseActive) cmodein |= ENABLE_MOUSE_INPUT; #endif - cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); + cmodeout &= ~( +#ifdef FEAT_TERMGUICOLORS + /* Do not turn off the ENABLE_PROCESSRD_OUTPUT flag when using + * VTP. */ + ((vtp_working) ? 0 : ENABLE_PROCESSED_OUTPUT) | +#else + ENABLE_PROCESSED_OUTPUT | +#endif + ENABLE_WRAP_AT_EOL_OUTPUT); bEnableHandler = TRUE; } else /* cooked */ @@ -5448,6 +5535,7 @@ termcap_mode_start(void) * to restore the actual contents of the buffer. */ RestoreConsoleBuffer(&g_cbTermcap, FALSE); + reset_console_color_rgb(); SetConsoleWindowInfo(g_hConOut, TRUE, &g_cbTermcap.Info.srWindow); Rows = g_cbTermcap.Info.dwSize.Y; Columns = g_cbTermcap.Info.dwSize.X; @@ -5460,6 +5548,7 @@ termcap_mode_start(void) * size. We will use this as the size of our editing environment. */ ClearConsoleBuffer(g_attrCurrent); + set_console_color_rgb(); ResizeConBufAndWindow(g_hConOut, Columns, Rows); } @@ -5508,6 +5597,7 @@ termcap_mode_end(void) cb = &g_cbNonTermcap; #endif RestoreConsoleBuffer(cb, p_rs); + reset_console_color_rgb(); SetConsoleCursorInfo(g_hConOut, &g_cci); if (p_rs || exiting) @@ -5562,7 +5652,11 @@ clear_chars( DWORD dwDummy; FillConsoleOutputCharacter(g_hConOut, ' ', n, coord, &dwDummy); - FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy); + + if (!USE_VTP) + FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, n, coord, &dwDummy); + else + FillConsoleOutputAttribute(g_hConOut, 0, n, coord, &dwDummy); } @@ -5573,7 +5667,15 @@ clear_chars( clear_screen(void) { g_coord.X = g_coord.Y = 0; - clear_chars(g_coord, Rows * Columns); + + if (!USE_VTP) + clear_chars(g_coord, Rows * Columns); + else + { + set_console_color_rgb(); + gotoxy(1, 1); + vtp_printf("\033[2J"); + } } @@ -5583,8 +5685,20 @@ clear_screen(void) static void clear_to_end_of_display(void) { - clear_chars(g_coord, (Rows - g_coord.Y - 1) + COORD save = g_coord; + + if (!USE_VTP) + clear_chars(g_coord, (Rows - g_coord.Y - 1) * Columns + (Columns - g_coord.X)); + else + { + set_console_color_rgb(); + gotoxy(g_coord.X + 1, g_coord.Y + 1); + vtp_printf("\033[0J"); + + gotoxy(save.X + 1, save.Y + 1); + g_coord = save; + } } @@ -5594,7 +5708,19 @@ clear_to_end_of_display(void) static void clear_to_end_of_line(void) { - clear_chars(g_coord, Columns - g_coord.X); + COORD save = g_coord; + + if (!USE_VTP) + clear_chars(g_coord, Columns - g_coord.X); + else + { + set_console_color_rgb(); + gotoxy(g_coord.X + 1, g_coord.Y + 1); + vtp_printf("\033[0K"); + + gotoxy(save.X + 1, save.Y + 1); + g_coord = save; + } } @@ -5633,6 +5759,9 @@ set_scroll_region( g_srScrollRegion.Top = top; g_srScrollRegion.Right = right; g_srScrollRegion.Bottom = bottom; + + if (USE_VTP) + vtp_printf("\033[%d;%dr", top + 1, bottom + 1); } @@ -5654,10 +5783,20 @@ insert_lines(unsigned cLines) source.Right = g_srScrollRegion.Right; source.Bottom = g_srScrollRegion.Bottom - cLines; - fill.Char.AsciiChar = ' '; - fill.Attributes = g_attrCurrent; - - ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill); + if (!USE_VTP) + { + fill.Char.AsciiChar = ' '; + fill.Attributes = g_attrCurrent; + + ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill); + } + else + { + set_console_color_rgb(); + + gotoxy(1, source.Top + 1); + vtp_printf("\033[%dT", cLines); + } /* Here we have to deal with a win32 console flake: If the scroll * region looks like abc and we scroll c to a and fill with d we get @@ -5696,10 +5835,20 @@ delete_lines(unsigned cLines) source.Right = g_srScrollRegion.Right; source.Bottom = g_srScrollRegion.Bottom; - fill.Char.AsciiChar = ' '; - fill.Attributes = g_attrCurrent; - - ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill); + if (!USE_VTP) + { + fill.Char.AsciiChar = ' '; + fill.Attributes = g_attrCurrent; + + ScrollConsoleScreenBuffer(g_hConOut, &source, NULL, dest, &fill); + } + else + { + set_console_color_rgb(); + + gotoxy(1, source.Top + 1); + vtp_printf("\033[%dS", cLines); + } /* Here we have to deal with a win32 console flake: If the scroll * region looks like abc and we scroll c to a and fill with d we get @@ -5735,7 +5884,11 @@ gotoxy( /* external cursor coords are 1-based; internal are 0-based */ g_coord.X = x - 1; g_coord.Y = y - 1; - SetConsoleCursorPosition(g_hConOut, g_coord); + + if (!USE_VTP) + SetConsoleCursorPosition(g_hConOut, g_coord); + else + vtp_printf("\033[%d;%dH", y, x); } @@ -5757,7 +5910,10 @@ textcolor(WORD wAttr) { g_attrCurrent = (g_attrCurrent & 0xf0) + (wAttr & 0x0f); - SetConsoleTextAttribute(g_hConOut, g_attrCurrent); + if (!USE_VTP) + SetConsoleTextAttribute(g_hConOut, g_attrCurrent); + else + vtp_sgr_bulk(wAttr); } @@ -5766,7 +5922,10 @@ textbackground(WORD wAttr) { g_attrCurrent = (g_attrCurrent & 0x0f) + ((wAttr & 0x0f) << 4); - SetConsoleTextAttribute(g_hConOut, g_attrCurrent); + if (!USE_VTP) + SetConsoleTextAttribute(g_hConOut, g_attrCurrent); + else + vtp_sgr_bulk(wAttr); } @@ -5776,7 +5935,10 @@ textbackground(WORD wAttr) static void normvideo(void) { - textattr(g_attrDefault); + if (!USE_VTP) + textattr(g_attrDefault); + else + vtp_sgr_bulk(0); } @@ -5789,6 +5951,7 @@ static WORD g_attrPreStandout = 0; standout(void) { g_attrPreStandout = g_attrCurrent; + textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY)); } @@ -5800,10 +5963,9 @@ standout(void) standend(void) { if (g_attrPreStandout) - { textattr(g_attrPreStandout); - g_attrPreStandout = 0; - } + + g_attrPreStandout = 0; } @@ -5818,7 +5980,11 @@ mch_set_normal_colors(void) cterm_normal_fg_color = (g_attrDefault & 0xf) + 1; cterm_normal_bg_color = ((g_attrDefault >> 4) & 0xf) + 1; - if (T_ME[0] == ESC && T_ME[1] == '|') + if ( +#ifdef FEAT_TERMGUICOLORS + !p_tgc && +#endif + T_ME[0] == ESC && T_ME[1] == '|') { p = T_ME + 2; n = getdigits(&p); @@ -5828,6 +5994,10 @@ mch_set_normal_colors(void) cterm_normal_bg_color = ((n >> 4) & 0xf) + 1; } } +#ifdef FEAT_TERMGUICOLORS + cterm_normal_fg_gui_color = INVALCOLOR; + cterm_normal_bg_gui_color = INVALCOLOR; +#endif } @@ -5851,7 +6021,8 @@ visual_bell(void) coordOrigin, &dwDummy); Sleep(15); /* wait for 15 msec */ - WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns, + if (!USE_VTP) + WriteConsoleOutputAttribute(g_hConOut, oldattrs, Rows * Columns, coordOrigin, &dwDummy); vim_free(oldattrs); } @@ -5901,14 +6072,24 @@ write_chars( unicodebuf, unibuflen); cells = mb_string2cells(pchBuf, cbToWrite); - FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells, - 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 = 1; + + if (!USE_VTP) + { + FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cells, + 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 = 1; + } + else + { + if (WriteConsoleW(g_hConOut, unicodebuf, length, &cchwritten, + NULL) == 0 || cchwritten == 0) + cchwritten = 1; + } if (cchwritten == length) { @@ -5927,14 +6108,23 @@ write_chars( else #endif { - FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite, - coord, &written); - /* When writing fails or didn't write a single character, pretend one - * character was written, otherwise we get stuck. */ - if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite, - coord, &written) == 0 - || written == 0) - written = 1; + if (!USE_VTP) + { + FillConsoleOutputAttribute(g_hConOut, g_attrCurrent, cbToWrite, + coord, &written); + /* When writing fails or didn't write a single character, pretend one + * character was written, otherwise we get stuck. */ + if (WriteConsoleOutputCharacter(g_hConOut, (LPCSTR)pchBuf, cbToWrite, + coord, &written) == 0 + || written == 0) + written = 1; + } + else + { + if (WriteConsole(g_hConOut, (LPCSTR)pchBuf, cbToWrite, &written, + NULL) == 0 || written == 0) + written = 1; + } g_coord.X += (SHORT) written; } @@ -6060,67 +6250,76 @@ mch_write( char_u *old_s = s; #endif char_u *p; - int arg1 = 0, arg2 = 0; + int arg1 = 0, arg2 = 0, argc = 0, args[16]; switch (s[2]) { - /* one or two numeric arguments, separated by ';' */ - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - p = s + 2; - arg1 = getdigits(&p); /* no check for length! */ - if (p > s + len) - break; - - if (*p == ';') + p = s + 1; + do { ++p; - arg2 = getdigits(&p); /* no check for length! */ + args[argc] = getdigits(&p); + argc += (argc < 15) ? 1 : 0; if (p > s + len) break; - - if (*p == 'H') - gotoxy(arg2, arg1); - else if (*p == 'r') - set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1); + } while (*p == ';'); + + if (p > s + len) + break; + + arg1 = args[0]; + arg2 = args[1]; + if (*p == 'm') + { + if (argc == 1 && args[0] == 0) + normvideo(); + else if (argc == 1) + { + if (USE_VTP) + textcolor((WORD) arg1); + else + textattr((WORD) arg1); + } + else if (USE_VTP) + vtp_sgr_bulks(argc, args); } - else if (*p == 'A') + else if (argc == 2 && *p == 'H') { - /* move cursor up arg1 lines in same column */ + gotoxy(arg2, arg1); + } + else if (argc == 2 && *p == 'r') + { + set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1); + } + else if (argc == 1 && *p == 'A') + { gotoxy(g_coord.X + 1, max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1); } - else if (*p == 'C') + else if (argc == 1 && *p == 'b') { - /* move cursor right arg1 columns in same line */ + textbackground((WORD) arg1); + } + else if (argc == 1 && *p == 'C') + { gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1, g_coord.Y + 1); } - else if (*p == 'H') + else if (argc == 1 && *p == 'f') + { + textcolor((WORD) arg1); + } + else if (argc == 1 && *p == 'H') { gotoxy(1, arg1); } - else if (*p == 'L') + else if (argc == 1 && *p == 'L') { insert_lines(arg1); } - else if (*p == 'm') - { - if (arg1 == 0) - normvideo(); - else - textattr((WORD) arg1); - } - else if (*p == 'f') - { - textcolor((WORD) arg1); - } - else if (*p == 'b') - { - textbackground((WORD) arg1); - } - else if (*p == 'M') + else if (argc == 1 && *p == 'M') { delete_lines(arg1); } @@ -6129,11 +6328,7 @@ mch_write( s = p + 1; break; - - /* Three-character escape sequences */ - case 'A': - /* move cursor up one line in same column */ gotoxy(g_coord.X + 1, max(g_srScrollRegion.Top, g_coord.Y - 1) + 1); goto got3; @@ -6143,7 +6338,6 @@ mch_write( goto got3; case 'C': - /* move cursor right one column in same line */ gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1, g_coord.Y + 1); goto got3; @@ -7242,3 +7436,186 @@ mch_setenv(char *var, char *value, int x return 0; } + +#ifndef FEAT_GUI_W32 + +/* + * Support for 256 colors and 24-bit colors was added in Windows 10 + * version 1703 (Creators update). + */ +# define VTP_FIRST_SUPPORT_BUILD MAKE_VER(10, 0, 15063) + + static void +vtp_init(void) +{ + DWORD ver, mode; + HMODULE hKerneldll; + DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi; + + ver = get_build_number(); + vtp_working = (ver >= VTP_FIRST_SUPPORT_BUILD) ? 1 : 0; + GetConsoleMode(g_hConOut, &mode); + mode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + if (SetConsoleMode(g_hConOut, mode) == 0) + vtp_working = 0; + + /* Use functions supported from Vista */ + hKerneldll = GetModuleHandle("kernel32.dll"); + if (hKerneldll != NULL) + { + pGetConsoleScreenBufferInfoEx = + (PfnGetConsoleScreenBufferInfoEx)GetProcAddress( + hKerneldll, "GetConsoleScreenBufferInfoEx"); + pSetConsoleScreenBufferInfoEx = + (PfnSetConsoleScreenBufferInfoEx)GetProcAddress( + hKerneldll, "SetConsoleScreenBufferInfoEx"); + if (pGetConsoleScreenBufferInfoEx != NULL + && pSetConsoleScreenBufferInfoEx != NULL) + has_csbiex = TRUE; + } + + csbi.cbSize = sizeof(csbi); + if (has_csbiex) + pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi); + save_console_bg_rgb = (guicolor_T)csbi.ColorTable[0]; + save_console_fg_rgb = (guicolor_T)csbi.ColorTable[7]; + + set_console_color_rgb(); +} + + static void +vtp_exit(void) +{ + reset_console_color_rgb(); +} + + static int +vtp_printf( + char *format, + ...) +{ + 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; +} + + static void +vtp_sgr_bulk( + int arg) +{ + int args[1]; + + args[0] = arg; + 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); +} + + static void +set_console_color_rgb(void) +{ +# ifdef FEAT_TERMGUICOLORS + DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi; + int id; + guicolor_T fg = INVALCOLOR; + guicolor_T bg = INVALCOLOR; + + if (!USE_VTP) + return; + + id = syn_name2id((char_u *)"Normal"); + if (id > 0) + syn_id2colors(id, &fg, &bg); + if (fg == INVALCOLOR) + fg = 0xc0c0c0; /* white text */ + if (bg == INVALCOLOR) + bg = 0x000000; /* black background */ + fg = (GetRValue(fg) << 16) | (GetGValue(fg) << 8) | GetBValue(fg); + bg = (GetRValue(bg) << 16) | (GetGValue(bg) << 8) | GetBValue(bg); + + csbi.cbSize = sizeof(csbi); + if (has_csbiex) + pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi); + + csbi.cbSize = sizeof(csbi); + csbi.srWindow.Right += 1; + csbi.srWindow.Bottom += 1; + csbi.ColorTable[0] = (COLORREF)bg; + csbi.ColorTable[7] = (COLORREF)fg; + if (has_csbiex) + pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi); +# endif +} + + static void +reset_console_color_rgb(void) +{ +# ifdef FEAT_TERMGUICOLORS + DYN_CONSOLE_SCREEN_BUFFER_INFOEX csbi; + + csbi.cbSize = sizeof(csbi); + if (has_csbiex) + pGetConsoleScreenBufferInfoEx(g_hConOut, &csbi); + + csbi.cbSize = sizeof(csbi); + csbi.srWindow.Right += 1; + csbi.srWindow.Bottom += 1; + csbi.ColorTable[0] = (COLORREF)save_console_bg_rgb; + csbi.ColorTable[7] = (COLORREF)save_console_fg_rgb; + if (has_csbiex) + pSetConsoleScreenBufferInfoEx(g_hConOut, &csbi); +# endif +} + + void +control_console_color_rgb(void) +{ + if (USE_VTP) + set_console_color_rgb(); + else + reset_console_color_rgb(); +} + + int +has_vtp_working(void) +{ + return vtp_working; +} + + int +use_vtp(void) +{ + return USE_VTP; +} + +#endif diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -42,6 +42,7 @@ void mch_set_shellsize(void); void mch_new_shellsize(void); void mch_set_winsize_now(void); int mch_call_shell(char_u *cmd, int options); +void win32_build_env(dict_T *env, garray_T *gap, int is_terminal); void mch_job_start(char *cmd, job_T *job, jobopt_T *options); char *mch_job_status(job_T *job); job_T *mch_detect_ended_job(job_T *job_list); @@ -67,5 +68,7 @@ void used_file_arg(char *name, int liter void set_alist_count(void); void fix_arg_enc(void); int mch_setenv(char *var, char *value, int x); -void win32_build_env(dict_T *l, garray_T *gap, int is_terminal); +void control_console_color_rgb(void); +int has_vtp_working(void); +int use_vtp(void); /* vim: set ft=c : */ diff --git a/src/proto/term.pro b/src/proto/term.pro --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -73,6 +73,7 @@ void show_termcodes(void); int show_one_termcode(char_u *name, char_u *code, int printit); char_u *translate_mapping(char_u *str, int expmap); void update_tcap(int attr); +void swap_tcap(void); guicolor_T gui_get_color_cmn(char_u *name); guicolor_T gui_get_rgb_color_cmn(int r, int g, int b); /* vim: set ft=c : */ diff --git a/src/screen.c b/src/screen.c --- a/src/screen.c +++ b/src/screen.c @@ -2177,6 +2177,25 @@ win_update(win_T *wp) * End of loop over all window lines. */ +#ifdef FEAT_VTP + /* Rewrite the character at the end of the screen line. */ + if (use_vtp()) + { + int i; + + for (i = 0; i < Rows; ++i) +# ifdef FEAT_MBYTE + if (enc_utf8) + if ((*mb_off2cells)(LineOffset[i] + Columns - 2, + LineOffset[i] + screen_Columns) > 1) + screen_draw_rectangle(i, Columns - 2, 1, 2, FALSE); + else + screen_draw_rectangle(i, Columns - 1, 1, 1, FALSE); + else +# endif + screen_char(LineOffset[i] + Columns - 1, i, Columns - 1); + } +#endif if (idx > wp->w_lines_valid) wp->w_lines_valid = idx; diff --git a/src/syntax.c b/src/syntax.c --- a/src/syntax.c +++ b/src/syntax.c @@ -8927,6 +8927,10 @@ get_cterm_attr_idx(int attr, int fg, int attrentry_T at_en; vim_memset(&at_en, 0, sizeof(attrentry_T)); +#ifdef FEAT_TERMGUICOLORS + at_en.ae_u.cterm.fg_rgb = INVALCOLOR; + at_en.ae_u.cterm.bg_rgb = INVALCOLOR; +#endif at_en.ae_attr = attr; at_en.ae_u.cterm.fg_color = fg; at_en.ae_u.cterm.bg_color = bg; @@ -9566,6 +9570,23 @@ set_hl_attr( at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg; at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg; # ifdef FEAT_TERMGUICOLORS +# ifdef WIN3264 + { + int id; + guicolor_T fg, bg; + + id = syn_name2id((char_u *)"Normal"); + if (id > 0) + { + syn_id2colors(id, &fg, &bg); + if (sgp->sg_gui_fg == INVALCOLOR) + sgp->sg_gui_fg = fg; + if (sgp->sg_gui_bg == INVALCOLOR) + sgp->sg_gui_bg = bg; + } + + } +# endif at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg); at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg); # endif diff --git a/src/term.c b/src/term.c --- a/src/term.c +++ b/src/term.c @@ -76,7 +76,6 @@ struct builtin_term static struct builtin_term *find_builtin_term(char_u *name); static void parse_builtin_tcap(char_u *s); -static void term_color(char_u *s, int n); static void gather_termleader(void); #ifdef FEAT_TERMRESPONSE static void req_codes_from_term(void); @@ -600,6 +599,10 @@ static struct builtin_term builtin_termc # else {(int)KS_CS, "\033|%i%d;%dr"},/* scroll region */ # endif +# ifdef FEAT_TERMGUICOLORS + {(int)KS_8F, "\033|38;2;%lu;%lu;%lum"}, + {(int)KS_8B, "\033|48;2;%lu;%lu;%lum"}, +# endif {K_UP, "\316H"}, {K_DOWN, "\316P"}, @@ -2007,6 +2010,11 @@ set_termname(char_u *term) may_req_termresponse(); #endif +#if defined(WIN3264) && !defined(FEAT_GUI) && defined(FEAT_TERMGUICOLORS) + if (STRCMP(term, "win32") == 0) + set_color_count((p_tgc) ? 256 : 16); +#endif + return OK; } @@ -2818,31 +2826,12 @@ term_set_winsize(int height, int width) } #endif - void -term_fg_color(int n) -{ - /* Use "AF" termcap entry if present, "Sf" entry otherwise */ - if (*T_CAF) - term_color(T_CAF, n); - else if (*T_CSF) - term_color(T_CSF, n); -} - - void -term_bg_color(int n) -{ - /* Use "AB" termcap entry if present, "Sb" entry otherwise */ - if (*T_CAB) - term_color(T_CAB, n); - else if (*T_CSB) - term_color(T_CSB, n); -} - static void term_color(char_u *s, int n) { char buf[20]; - int i = 2; /* index in s[] just after [ or CSI */ + int i = *s == CSI ? 1 : 2; + /* index in s[] just after [ or CSI */ /* Special handling of 16 colors, because termcap can't handle it */ /* Also accept "\e[3%dm" for TERMINFO, it is sometimes used */ @@ -2869,6 +2858,26 @@ term_color(char_u *s, int n) OUT_STR(tgoto((char *)s, 0, n)); } + void +term_fg_color(int n) +{ + /* Use "AF" termcap entry if present, "Sf" entry otherwise */ + if (*T_CAF) + term_color(T_CAF, n); + else if (*T_CSF) + term_color(T_CSF, n); +} + + void +term_bg_color(int n) +{ + /* Use "AB" termcap entry if present, "Sb" entry otherwise */ + if (*T_CAB) + term_color(T_CAB, n); + else if (*T_CSB) + term_color(T_CSB, n); +} + #if defined(FEAT_TERMGUICOLORS) || defined(PROTO) #define RED(rgb) (((long_u)(rgb) >> 16) & 0xFF) @@ -6614,6 +6623,106 @@ update_tcap(int attr) ++p; } } + +struct ks_tbl_s +{ + int code; /* value of KS_ */ + char *vtp; /* code in vtp mode */ + char *buf; /* buffer in non-vtp mode */ + char *vbuf; /* buffer in vtp mode */ +}; + +static struct ks_tbl_s ks_tbl[] = +{ + {(int)KS_ME, "\033|0m" }, /* normal */ + {(int)KS_MR, "\033|7m" }, /* reverse */ + {(int)KS_MD, "\033|1m" }, /* bold */ + {(int)KS_SO, "\033|91m"}, /* standout: bright red text */ + {(int)KS_SE, "\033|39m"}, /* standout end: default color */ + {(int)KS_CZH, "\033|95m"}, /* italic: bright magenta text */ + {(int)KS_CZR, "\033|0m",}, /* italic end */ + {(int)KS_US, "\033|4m",}, /* underscore */ + {(int)KS_UE, "\033|24m"}, /* underscore end */ + {(int)KS_NAME, NULL} +}; + + static struct builtin_term * +find_first_tcap( + char_u *name, + int code) +{ + struct builtin_term *p; + + p = find_builtin_term(name); + while (p->bt_string != NULL) + { + if (p->bt_entry == code) + return p; + p++; + } + return NULL; +} + +/* + * For Win32 console: replace the sequence immediately after termguicolors. + */ + void +swap_tcap(void) +{ +# ifdef FEAT_TERMGUICOLORS + static int init = 0; + static int last_tgc; + struct ks_tbl_s *ks; + struct builtin_term *bt; + + /* buffer initialization */ + if (init == 0) + { + ks = ks_tbl; + while (ks->vtp != NULL) + { + bt = find_first_tcap(DEFAULT_TERM, ks->code); + ks->buf = bt->bt_string; + ks->vbuf = ks->vtp; + ks++; + } + init++; + last_tgc = p_tgc; + return; + } + + if (last_tgc != p_tgc) + { + if (p_tgc) + { + /* switch to special character sequence */ + ks = ks_tbl; + while (ks->vtp != NULL) + { + bt = find_first_tcap(DEFAULT_TERM, ks->code); + ks->buf = bt->bt_string; + bt->bt_string = ks->vbuf; + ks++; + } + } + else + { + /* switch to index color */ + ks = ks_tbl; + while (ks->vtp != NULL) + { + bt = find_first_tcap(DEFAULT_TERM, ks->code); + ks->vbuf = bt->bt_string; + bt->bt_string = ks->buf; + ks++; + } + } + + last_tgc = p_tgc; + } +# endif +} + #endif #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO) diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim --- a/src/testdir/gen_opt_test.vim +++ b/src/testdir/gen_opt_test.vim @@ -129,6 +129,7 @@ let test_values = { \ 'switchbuf': [['', 'useopen', 'split,newtab'], ['xxx']], \ 'tagcase': [['smart', 'match'], ['', 'xxx', 'smart,match']], \ 'term': [[], []], + \ 'termguicolors': [[], []], \ 'termsize': [['', '24x80', '0x80', '32x0', '0x0'], ['xxx', '80', '8ax9', '24x80b']], \ 'termencoding': [has('gui_gtk') ? [] : ['', 'utf-8'], ['xxx']], \ 'toolbar': [['', 'icons', 'text'], ['xxx']], diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -699,6 +699,13 @@ static char *(features[]) = #else "-vreplace", #endif +#ifdef WIN3264 +# ifdef FEAT_VTP + "+vtp", +# else + "-vtp", +# endif +#endif #ifdef FEAT_WILDIGN "+wildignore", #else @@ -772,6 +779,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1531, +/**/ 1530, /**/ 1529,