diff --git a/doc/emacs/cmdargs.texi b/doc/emacs/cmdargs.texi index 3bcf4949581..5a3c1c14ab1 100644 --- a/doc/emacs/cmdargs.texi +++ b/doc/emacs/cmdargs.texi @@ -1024,19 +1024,26 @@ colored display. @itemx ansi8 Turn on the color support unconditionally, and use color commands specified by the ANSI escape sequences for the 8 standard colors. +@item 8bit +Turn on support for 8-bit (256 color) display if available. +Currently this option is effective on MS-Windows (10+) only. +On other systems, maximal color support is enabled automatically. +@item 24bit +Turn on support for 24-bit (true color) display if available. +Currently this option is effective on MS-Windows (10+) only. +On other systems, 24-bit color is enabled automatically if supported. @item @var{num} Use color mode for @var{num} colors. If @var{num} is @minus{}1, turn off color support (equivalent to @samp{never}); if it is 0, use the default color support for this terminal (equivalent to @samp{auto}); otherwise use an appropriate standard mode for @var{num} colors. Depending on your terminal's capabilities, Emacs might be able to turn -on a color mode for 8, 16, 88, or 256 as the value of @var{num}. If -there is no mode that supports @var{num} colors, Emacs acts as if -@var{num} were 0, i.e., it uses the terminal's default color support -mode. +on a color mode for 8, 16, 88, 256, or 16777216 as the value of @var{num}. +If there is no mode that supports @var{num} colors, Emacs acts as if +@var{num} were 0, i.e., it uses the terminal's default color support mode. @end table -This option has no effect on MS-Windows and MS-DOS. +This option has no effect on MS-DOS, nor MS-Windows prior to Windows 10. @cindex colors on character terminal, changing during session @cindex character terminal, change color mode diff --git a/doc/emacs/msdos.texi b/doc/emacs/msdos.texi index 26b6ccbce09..203df57c7c1 100644 --- a/doc/emacs/msdos.texi +++ b/doc/emacs/msdos.texi @@ -1200,6 +1200,33 @@ you should set its value in your init file (@pxref{Init File}), either directly or via @kbd{M-x customize-variable}, which lets you save the customized value, see @ref{Saving Customizations}. +@findex w32-use-virtual-terminal +@cindex Windows Terminal, Windows Console, MS-Windows + The implementation of display functionality for Windows Console +differs from the implementation for other terminal emulators, +because historically, Windows required use of an idiosyncractic API. +That API limited Windows Console display of Emacs to 16 basic colors. +With the introduction of Windows Terminal, Microsoft implemented +support for ANSI control sequences, modelled on the VT100 and Xterm, +as well as 24-bit RBG color display. + + The functions @code{w32-use-virtual-terminal} and +@code{w32-use-virtual-terminal-p} can be used to set and inspect +(respectively) an internal variable which determines whether this newer +mechanism is used for display, or the older one. The internal variable is +automatically set based on your terminal's capabilities on startup. +By default, 24-bit RGB color will be used, but other (8, 16, 256) color +spaces may be used, by passing the @code{--color} command line argument, +or setting the value of the @code{tty-color-mode} frame parameter. + + @code{'(w32-use-virtual-terminal-p)} evaluates to @code{t} if and only if +the internal variable has a non-zero numerical value, and otherwise to +@code{nil}. If it evaluates to @code{t}, ANSI escape sequences are used +for color, otherwise, the older mechanism is used. The internal variable +can be set by evaluating @code{(w32-use-virtual-terminal x)}, where @code{x} is +@code{t} or @code{nil}: if @code{x} is @code{t} and the feature is supported by +your terminal, it will be enabled. Otherwise, the feature will be disabled. + @ifnottex @include msdos-xtra.texi @end ifnottex diff --git a/etc/NEWS b/etc/NEWS index b3a497c351a..90dbca7501b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -4630,6 +4630,18 @@ Accordingly, we have revised our recommendations for a suitable DJGPP toolchain to GCC 14.2.0 and Binutils 2.35.1 in lieu of GCC 3.4.x and Binutils 2.26. ++++ +** Windows Terminal can now display 256 and 24-bit RGB color. +Previously, terminal sessions on Windows supported display of 16 colors. +There is now support for 8-bit (256 color) and 24-bit RGB (true color). +The new mechanism will be enabled automatically when supported. +It defaults to 24-bit RGB color, but can be set to 8, 16, '8bit, or '24bit +by passing the '--color' flag or setting the 'tty-color-mode' frame parameter. +Use of the new mechanism is controlled by an internal variable that can be +set and inspected via the functions 'w32-use-virtual-terminal' and +'w32-use-virtual-terminal-p' (respectively). See the manual entry specific +to MS-Windows for more details. + ---------------------------------------------------------------------- This file is part of GNU Emacs. diff --git a/lisp/faces.el b/lisp/faces.el index fbc81af1b51..6fe94927290 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -2405,11 +2405,15 @@ If you set `term-file-prefix' to nil, this function does nothing." ;; Called from C function init_display to initialize faces of the ;; dumped terminal frame on startup. - +(declare-function w32-tty-setup-colors "term/w32console" ()) (defun tty-set-up-initial-frame-faces () - (let ((frame (selected-frame))) - (frame-set-background-mode frame t) - (face-set-after-frame-default frame))) + (progn + (when (and (eq system-type 'windows-nt) + (featurep 'term/w32console)) + (w32-tty-setup-colors)) + (let ((frame (selected-frame))) + (frame-set-background-mode frame t) + (face-set-after-frame-default frame)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/lisp/startup.el b/lisp/startup.el index adaea013604..decd25a6232 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -958,7 +958,7 @@ to prepare for opening the first frame (e.g. open a connection to an X server)." (push (cons 'tty-color-mode (cond ((numberp argval) argval) - ((string-match "-?[0-9]+" argval) + ((string-match "-?[0-9]+$" argval) (string-to-number argval)) (t (intern argval)))) default-frame-alist)) diff --git a/lisp/term/tty-colors.el b/lisp/term/tty-colors.el index 183464befa5..a0450bc46bb 100644 --- a/lisp/term/tty-colors.el +++ b/lisp/term/tty-colors.el @@ -764,7 +764,9 @@ (auto . 0) (ansi8 . 8) (always . 8) - (yes . 8)) + (yes . 8) + (8bit . 256) + (24bit . 16777216)) "An alist of supported standard tty color modes and their aliases.") (defun tty-color-alist (&optional _frame) diff --git a/lisp/term/w32console.el b/lisp/term/w32console.el index 79b84a9f752..2985cf7e39b 100644 --- a/lisp/term/w32console.el +++ b/lisp/term/w32console.el @@ -46,9 +46,77 @@ "A list of VGA console colors, their indices and 16-bit RGB values.") (declare-function x-setup-function-keys "term/common-win" (frame)) -(declare-function get-screen-color "w32console.c" ()) +(declare-function get-screen-color "w32console.c" (&optional vtp)) +(declare-function set-screen-color "w32console.c" (&optional vtp)) (declare-function w32-get-console-codepage "w32proc.c" ()) (declare-function w32-get-console-output-codepage "w32proc.c" ()) +(declare-function w32-use-virtual-terminal "w32console.c" (enable)) +(declare-function w32-use-virtual-terminal-p "w32console.c" ()) + +(defun w32-tty-set-base-colors (vtp) + "Re-order `w32-tty-standard-colors' based on the value of VTP." + (let ((seq + (if vtp + '("black" "red" "green" "brown" + "blue" "magenta" "cyan" "lightgray" + "darkgray" "lightred" "lightgreen" "yellow" + "lightblue" "lightmagenta" "lightcyan" "white") + '("black" "blue" "green" "cyan" + "red" "magenta" "brown" "lightgray" + "darkgray" "lightblue" "lightgreen" "lightcyan" + "lightred" "lightmagenta" "yellow" "white")))) + (setq w32-tty-standard-colors + (mapcar + (lambda (n) (let ((c (assoc n w32-tty-standard-colors))) + (cons n (cons (seq-position seq n) (cddr c))))) + seq)))) + +(defun w32-tty-define-base-colors () + "Defines base 16-color space for w32 tty display." + (let* ((colors w32-tty-standard-colors) + (nbase (length colors)) + (color (car colors))) + (progn (while colors + (tty-color-define (car color) (cadr color) (cddr color)) + (setq colors (cdr colors) + color (car colors))) + nbase))) + +(defun w32-tty-define-8bit-colors () + "Defines 8-bit color space for w32 tty display." + (let ((r 0) (b 0) (g 0) + (n (- 256 (w32-tty-define-base-colors))) + (convert-to-16bit (lambda (prim) (logior prim (ash prim 8))))) + (while (> n 24) ; non-grey + (let ((i (- 256 n)) + (c (mapcar convert-to-16bit + (mapcar (lambda (x) (if (zerop x) 0 (+ (* x 40) 55))) + (list r g b))))) + (tty-color-define (format "color-%d" i) i c)) + (setq b (1+ b)) + (when (> b 5) (setq g (1+ g) b 0)) + (when (> g 5) (setq r (1+ r) g 0)) + (setq n (1- n))) + (while (> n 0) ; all-grey + (let* ((i (- 256 n)) + (v (funcall convert-to-16bit (+ 8 (* (- 24 n) 10)))) + (c (list v v v))) + (tty-color-define (format "color-%d" i) i c)) + (setq n (1- n))))) + +(defun w32-tty-define-24bit-colors () + "Defines 24-bit color space for w32 tty display." + (let ((i (w32-tty-define-base-colors))) + (mapc (lambda (c) (unless (assoc (car c) w32-tty-standard-colors) + (tty-color-define (car c) i (cdr c)) + (setq i (1+ i)))) + color-name-rgb-alist))) + +;; tty-color-define swaps indices for pixel values on 24bit display +(defun w32-tty-get-pixel (index) + "Convert a legacy color INDEX (0..15) into a pixel value." + (let ((color (nth index w32-tty-standard-colors))) + (or (tty-color-24bit (cddr color)) index))) (defun terminal-init-w32console () "Terminal initialization function for w32 console." @@ -56,43 +124,53 @@ (x-setup-function-keys (selected-frame)) ;; Set terminal and keyboard encodings to the current OEM codepage. (let ((oem-code-page-coding - (intern (format "cp%d" (w32-get-console-codepage)))) - (oem-code-page-output-coding - (intern (format "cp%d" (w32-get-console-output-codepage)))) - oem-cs-p oem-o-cs-p) - (setq oem-cs-p (coding-system-p oem-code-page-coding)) - (setq oem-o-cs-p (coding-system-p oem-code-page-output-coding)) - (when oem-cs-p - (set-keyboard-coding-system oem-code-page-coding) - (set-terminal-coding-system - (if oem-o-cs-p oem-code-page-output-coding oem-code-page-coding)) - ;; Since we changed the terminal encoding, we need to repeat - ;; the test for Unicode quotes being displayable. - (startup--setup-quote-display))) - (let* ((colors w32-tty-standard-colors) - (color (car colors))) - (tty-color-clear) - (while colors - (tty-color-define (car color) (cadr color) (cddr color)) - (setq colors (cdr colors) - color (car colors)))) - (clear-face-cache) - ;; Figure out what are the colors of the console window, and set up - ;; the background-mode correspondingly. - (let* ((screen-color (get-screen-color)) - (bg (cadr screen-color)) - (descr (tty-color-by-index bg)) - r g b bg-mode) - (setq r (nth 2 descr) - g (nth 3 descr) - b (nth 4 descr)) - (if (< (+ r g b) (* .6 (+ 65535 65535 65535))) - (setq bg-mode 'dark) - (setq bg-mode 'light)) - (set-terminal-parameter nil 'background-mode bg-mode)) + (intern (format "cp%d" (w32-get-console-codepage)))) + (oem-code-page-output-coding + (intern (format "cp%d" (w32-get-console-output-codepage)))) + oem-cs-p oem-o-cs-p) + (setq oem-cs-p (coding-system-p oem-code-page-coding)) + (setq oem-o-cs-p (coding-system-p oem-code-page-output-coding)) + (when oem-cs-p + (set-keyboard-coding-system oem-code-page-coding) + (set-terminal-coding-system + (if oem-o-cs-p oem-code-page-output-coding oem-code-page-coding)) + ;; Since we changed the terminal encoding, we need to repeat + ;; the test for Unicode quotes being displayable. + (startup--setup-quote-display))) (tty-set-up-initial-frame-faces) (run-hooks 'terminal-init-w32-hook)) +;; Called from tty-set-up-initial-frame-faces in faces.el +(defun w32-tty-setup-colors () + "Set up color definitions and frame parameters for w32 tty display." + (tty-color-clear) + (let ((ncolors (display-color-cells)) + (vtp (w32-use-virtual-terminal-p))) + (w32-tty-set-base-colors vtp) + (if vtp + (cond ((= ncolors 16777216) (w32-tty-define-24bit-colors)) + ((= ncolors 256) (w32-tty-define-8bit-colors)) + (t (w32-tty-define-base-colors))) + (w32-tty-define-base-colors)) + (clear-face-cache) + (let* ((screen-color (get-screen-color vtp)) + (fg (car screen-color)) + (bg (cadr screen-color)) + (bootstrap (and vtp (= ncolors 16777216) + (< fg 16) (< bg 16) (not (= 0 fg bg)))) + (fallback (and vtp (< ncolors 16777216) + (or (< ncolors fg) (< ncolors bg)))) + (screen-color (if fallback (get-screen-color t) screen-color)) + (fg (if bootstrap (w32-tty-get-pixel fg) (car screen-color))) + (bg (if bootstrap (w32-tty-get-pixel bg) (cadr screen-color))) + (bg-col (tty-color-by-index bg)) + (bg-dark (< (+ (nth 2 bg-col) (nth 3 bg-col) (nth 4 bg-col)) + (* .6 (+ 65535 65535 65535)))) + (bg-mode (if bg-dark 'dark 'light))) + (set-terminal-parameter nil 'background-mode bg-mode) + (when (or bootstrap fallback) + (set-screen-color fg bg t))))) + (provide 'term/w32console) ;;; w32console.el ends here diff --git a/src/term.c b/src/term.c index b5cf418450f..766eeebe5e8 100644 --- a/src/term.c +++ b/src/term.c @@ -2207,7 +2207,7 @@ TERMINAL does not refer to a text terminal. */) return make_fixnum (t ? t->display_info.tty->TN_max_colors : 0); } -#if !defined DOS_NT && !defined HAVE_ANDROID +#if !defined MSDOS && !defined HAVE_ANDROID /* Declare here rather than in the function, as in the rest of Emacs, to work around an HPUX compiler bug (?). See @@ -2246,7 +2246,7 @@ tty_default_color_capabilities (struct tty_display_info *tty, bool save) MODE's value is generally the number of colors which we want to support; zero means set up for the default capabilities, the ones we saw at init_tty time; -1 means turn off color support. */ -static void +void tty_setup_colors (struct tty_display_info *tty, int mode) { /* Canonicalize all negative values of MODE. */ @@ -2269,6 +2269,10 @@ tty_setup_colors (struct tty_display_info *tty, int mode) #ifdef TERMINFO tty->TS_set_foreground = "\033[3%p1%dm"; tty->TS_set_background = "\033[4%p1%dm"; +#elif WINDOWSNT + tty->TS_orig_pair = "\x1b[39m\x1b[49m"; + tty->TS_set_foreground = "\x1b[%lum"; + tty->TS_set_background = "\x1b[%lum"; #else tty->TS_set_foreground = "\033[3%dm"; tty->TS_set_background = "\033[4%dm"; @@ -2276,6 +2280,26 @@ tty_setup_colors (struct tty_display_info *tty, int mode) tty->TN_max_colors = 8; tty->TN_no_color_video = 0; break; +#ifdef WINDOWSNT + case 16: + tty->TN_max_colors = 16; + tty->TS_set_foreground = "\x1b[%lum"; + tty->TS_set_background = "\x1b[%lum"; + tty->TN_no_color_video = 0; + break; + case 256: + tty->TN_max_colors = 256; + tty->TS_set_foreground = "\x1b[38;5;%lum"; + tty->TS_set_background = "\x1b[48;5;%lum"; + tty->TN_no_color_video = 0; + break; + case 16777216: + tty->TN_max_colors = 16777216; + tty->TS_set_foreground = "\x1b[38;2;%lu;%lu;%lum"; + tty->TS_set_background = "\x1b[48;2;%lu;%lu;%lum"; + tty->TN_no_color_video = 0; + break; +#endif } } @@ -2312,7 +2336,7 @@ set_tty_color_mode (struct tty_display_info *tty, struct frame *f) } } -#endif /* !DOS_NT && !HAVE_ANDROID */ +#endif /* !MSDOS && !HAVE_ANDROID */ char * tty_type_name (Lisp_Object terminal) @@ -4644,6 +4668,22 @@ use the Bourne shell command 'TERM=...; export TERM' (C-shell:\n\ initialize_w32_display (terminal, &width, &height); + tty->TN_no_color_video = 0; + tty->TN_max_colors = 16777216; + tty->TS_orig_pair = "\x1b[39m\x1b[49m"; + tty->TS_set_foreground = "\x1b[38;2;%lu;%lu;%lum"; + tty->TS_set_background = "\x1b[48;2;%lu;%lu;%lum"; + + /* Save default color capabilities */ + tty_default_color_capabilities (tty, 1); + + tty->TS_enter_bold_mode = "\x1b[1m"; + tty->TS_enter_italic_mode = "\x1b[3m"; + tty->TS_enter_strike_through_mode = "\x1b[9m"; + tty->TS_enter_underline_mode = "\x1b[4m"; + tty->TS_enter_reverse_mode = "\x1b[7m"; + tty->TS_exit_attribute_mode = "\x1b[0m"; + FrameRows (tty) = height; FrameCols (tty) = width; tty->specified_window = height; @@ -4689,7 +4729,6 @@ use the Bourne shell command 'TERM=...; export TERM' (C-shell:\n\ don't think we're losing anything by turning it off. */ tty->line_ins_del_ok = 0; - tty->TN_max_colors = 16; /* Must be non-zero for tty-display-color-p. */ #endif /* DOS_NT */ #ifdef HAVE_GPM @@ -5205,11 +5244,11 @@ non-nil to enable this optimization. */); defsubr (&Stty_display_pixel_width); defsubr (&Stty_display_pixel_height); -#if !defined DOS_NT && !defined HAVE_ANDROID +#if !defined MSDOS && !defined HAVE_ANDROID default_orig_pair = NULL; default_set_foreground = NULL; default_set_background = NULL; -#endif /* !DOS_NT && !HAVE_ANDROID */ +#endif /* !MSDOS && !HAVE_ANDROID */ #ifndef HAVE_ANDROID encode_terminal_src = NULL; diff --git a/src/termchar.h b/src/termchar.h index 9d7c3a2a555..9a9001d5cef 100644 --- a/src/termchar.h +++ b/src/termchar.h @@ -249,4 +249,6 @@ struct input_event; extern Lisp_Object tty_handle_tab_bar_click (struct frame *, int, int, bool, struct input_event *); +extern void tty_setup_colors (struct tty_display_info *tty, int mode); + #endif /* EMACS_TERMCHAR_H */ diff --git a/src/w32console.c b/src/w32console.c index be5e0b92072..b0a2f0c1c0e 100644 --- a/src/w32console.c +++ b/src/w32console.c @@ -19,6 +19,9 @@ along with GNU Emacs. If not, see . */ /* Tim Fleehart (apollo@online.com) 1-17-92 Geoff Voelker (voelker@cs.washington.edu) 9-12-93 + Ewan Townshend (ewan@etown.dev) 2025-08 + * c. ~ 2025: 24bit RGB support in Windows (10+) Terminal + * https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences */ @@ -53,11 +56,17 @@ static void w32con_set_terminal_modes (struct terminal *t); static void w32con_update_begin (struct frame * f); static void w32con_update_end (struct frame * f); static WORD w32_face_attributes (struct frame *f, int face_id); +static int w32con_write_vt_seq (const char *); +static void turn_on_face (struct frame *, int face_id); +static void turn_off_face (struct frame *, int face_id); +static COORD w32con_get_cursor_coords (); static COORD cursor_coords; static HANDLE prev_screen, cur_screen; static WORD char_attr_normal; static DWORD prev_console_mode; +static int bg_normal; +static int fg_normal; static CONSOLE_CURSOR_INFO console_cursor_info; #ifndef USE_SEPARATE_SCREEN @@ -67,7 +76,10 @@ static CONSOLE_CURSOR_INFO prev_console_cursor; extern HANDLE keyboard_handle; HANDLE keyboard_handle; int w32_console_unicode_input; - +extern int w32_use_virtual_terminal; +int w32_use_virtual_terminal = 1; +extern struct tty_display_info *current_tty; +struct tty_display_info *current_tty = NULL; /* Setting this as the ctrl handler prevents emacs from being killed when someone hits ^C in a 'suspended' session (child shell). @@ -83,6 +95,38 @@ ctrl_c_handler (unsigned long type) && (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT)); } +#define DEFAULTP(p) \ + (p == FACE_TTY_DEFAULT_COLOR \ + || p == FACE_TTY_DEFAULT_FG_COLOR \ + || p == FACE_TTY_DEFAULT_BG_COLOR) + +#define SEQMAX 256 /* Arbitrary upper limit on VT sequence size */ + +#define SSPRINTF(buf, i, sz, fmt, ...) \ + do { \ + eassert (*i < sz && sz <= SEQMAX); \ + if (fmt && *i < sz && sz <= SEQMAX) \ + *i += snprintf (buf + *i, sz - *i, fmt, __VA_ARGS__); \ + } while (0) + +/* Writes virtual terminal sequence to screen */ +static int +w32con_write_vt_seq (const char *seq) +{ + char buf[SEQMAX]; + DWORD n = 0, k = 0; + SSPRINTF (buf, &n, SEQMAX, seq, NULL); + if (n) WriteConsole (cur_screen, (LPCSTR) buf, n, &k, NULL); + return k; +} + +static COORD +w32con_get_cursor_coords () +{ + CONSOLE_SCREEN_BUFFER_INFO info; + GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info); + return info.dwCursorPosition; +} /* Move the cursor to (ROW, COL) on FRAME. */ static void @@ -309,10 +353,11 @@ w32con_write_glyphs (struct frame *f, register struct glyph *string, register int len) { DWORD r; - WORD char_attr; LPCSTR conversion_buffer; struct coding_system *coding; + w32con_hide_cursor(); + if (len <= 0) return; @@ -342,8 +387,6 @@ w32con_write_glyphs (struct frame *f, register struct glyph *string, /* w32con_clear_end_of_line sets frame of glyphs to NULL. */ struct frame *attr_frame = face_id_frame ? face_id_frame : f; - /* Turn appearance modes of the face of the run on. */ - char_attr = w32_face_attributes (attr_frame, face_id); if (n == len) /* This is the last run. */ @@ -351,31 +394,45 @@ w32con_write_glyphs (struct frame *f, register struct glyph *string, conversion_buffer = (LPCSTR) encode_terminal_code (string, n, coding); if (coding->produced > 0) { - /* Compute the string's width on display by accounting for - character's width. FIXME: this doesn't handle character - compositions. */ - ptrdiff_t ncols = strwidth (coding->source, coding->src_bytes); - /* Set the attribute for these characters. */ - if (!FillConsoleOutputAttribute (cur_screen, char_attr, ncols, - cursor_coords, &r)) + if (w32_use_virtual_terminal) { - printf ("Failed writing console attributes: %lu\n", - GetLastError ()); - fflush (stdout); + turn_on_face (f, face_id); + WriteConsole (cur_screen, conversion_buffer, + coding->produced, &r, NULL); + turn_off_face (f, face_id); + cursor_coords = w32con_get_cursor_coords (); } - - /* Write the characters. */ - if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer, - coding->produced, cursor_coords, - &r)) + else { - printf ("Failed writing console characters: %lu\n", - GetLastError ()); - fflush (stdout); - } + /* Account for character width. + FIXME: this doesn't handle character compositions. */ + ptrdiff_t ncols = strwidth (coding->source, coding->src_bytes); - cursor_coords.X += ncols; - w32con_move_cursor (f, cursor_coords.Y, cursor_coords.X); + /* Turn appearance modes of the face of the run on. */ + WORD char_attr = w32_face_attributes (attr_frame, face_id); + + /* Set the attribute for these characters. */ + if (!FillConsoleOutputAttribute (cur_screen, char_attr, ncols, + cursor_coords, &r)) + { + printf ("Failed writing console attributes: %lu\n", + GetLastError ()); + fflush (stdout); + } + + /* Write the characters. */ + if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer, + coding->produced, cursor_coords, + &r)) + { + printf ("Failed writing console characters: %lu\n", + GetLastError ()); + fflush (stdout); + } + + cursor_coords.X += ncols; + w32con_move_cursor (f, cursor_coords.Y, cursor_coords.X); + } } len -= n; string += n; @@ -391,6 +448,8 @@ w32con_write_glyphs_with_face (struct frame *f, register int x, register int y, LPCSTR conversion_buffer; struct coding_system *coding; + w32con_hide_cursor(); + if (len <= 0) return; @@ -407,28 +466,42 @@ w32con_write_glyphs_with_face (struct frame *f, register int x, register int y, if (coding->produced > 0) { DWORD filled, written; - /* Compute the character attributes corresponding to the face. */ - DWORD char_attr = w32_face_attributes (f, face_id); - COORD start_coords; - /* Compute the string's width on display by accounting for - character's width. FIXME: this doesn't handle character - compositions. */ - ptrdiff_t ncols = strwidth (coding->source, coding->src_bytes); - - start_coords.X = x; - start_coords.Y = y; - /* Set the attribute for these characters. */ - if (!FillConsoleOutputAttribute (cur_screen, char_attr, ncols, - start_coords, &filled)) - DebPrint (("Failed writing console attributes: %d\n", GetLastError ())); + if (w32_use_virtual_terminal) + { + COORD saved_coords = cursor_coords; + w32con_move_cursor(f, y, x); + turn_on_face (f, face_id); + WriteConsole (cur_screen, conversion_buffer, + coding->produced, &written, NULL); + turn_off_face (f, face_id); + w32con_move_cursor(f, saved_coords.Y, saved_coords.X); + } else { - /* Write the characters. */ - if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer, - coding->produced, start_coords, - &written)) - DebPrint (("Failed writing console characters: %d\n", - GetLastError ())); + /* Compute the character attributes corresponding to the face. */ + DWORD char_attr = w32_face_attributes (f, face_id); + COORD start_coords; + + start_coords.X = x; + start_coords.Y = y; + + /* Account for character width. + FIXME: this doesn't handle character compositions. */ + ptrdiff_t ncols = strwidth (coding->source, coding->src_bytes); + + /* Set the attribute for these characters. */ + if (!FillConsoleOutputAttribute (cur_screen, char_attr, ncols, + start_coords, &filled)) + DebPrint (("Failed writing console attributes: %d\n", GetLastError ())); + else + { + /* Write the characters. */ + if (!WriteConsoleOutputCharacter (cur_screen, conversion_buffer, + coding->produced, start_coords, + &written)) + DebPrint (("Failed writing console characters: %d\n", + GetLastError ())); + } } } } @@ -529,6 +602,33 @@ w32con_delete_glyphs (struct frame *f, int n) scroll_line (f, n, LEFT); } +static void +w32con_setup_virtual_terminal (void) +{ + /* Disable unless 24bit color is supported (v. > 10.0.15063). */ + w32_use_virtual_terminal = w32_use_virtual_terminal + && (w32_major_version > 10 + || (w32_major_version == 10 + && (w32_minor_version > 0 || w32_build_number > 15063))); + + DWORD out_mode; + GetConsoleMode (cur_screen, &out_mode); + out_mode |= ENABLE_PROCESSED_OUTPUT; + out_mode |= DISABLE_NEWLINE_AUTO_RETURN; + + if (w32_use_virtual_terminal) + out_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + else + out_mode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING; + + int out_mode_set = SetConsoleMode (cur_screen, out_mode); + w32_use_virtual_terminal = w32_use_virtual_terminal && out_mode_set; + + int max_colors = w32_use_virtual_terminal ? 16777216 : 16; + tty_setup_colors (current_tty, max_colors); + + safe_calln (Qtty_set_up_initial_frame_faces); +} static void w32con_reset_terminal_modes (struct terminal *t) @@ -585,6 +685,7 @@ w32con_set_terminal_modes (struct terminal *t) /* Initialize input mode: interrupt_input off, no flow control, allow 8 bit character input, standard quit char. */ Fset_input_mode (Qnil, Qnil, make_fixnum (2), Qnil); + w32con_setup_virtual_terminal (); } /* hmmm... perhaps these let us bracket screen changes so that we can flush @@ -631,8 +732,6 @@ sys_tgetstr (char *cap, char **area) stubs from cm.c ***********************************************************************/ -extern struct tty_display_info *current_tty; -struct tty_display_info *current_tty = NULL; extern int cost; int cost = 0; @@ -736,6 +835,87 @@ w32_face_attributes (struct frame *f, int face_id) return char_attr; } +static void +turn_on_face (struct frame *f, int face_id) +{ + struct face *face = FACE_FROM_ID (f, face_id); + struct tty_display_info *tty = FRAME_TTY (f); + unsigned long fg = face->foreground; + unsigned long bg = face->background; + + /* construct combined VT sequence for face attributes */ + DWORD n = 0; + size_t sz = SEQMAX; + char seq[sz]; + sz--; + + if (face->tty_bold_p) + SSPRINTF (seq, &n, sz, tty->TS_enter_bold_mode, NULL); + if (face->tty_italic_p) + SSPRINTF (seq, &n, sz, tty->TS_enter_italic_mode, NULL); + if (face->tty_strike_through_p) + SSPRINTF (seq, &n, sz, tty->TS_enter_strike_through_mode, NULL); + if (face->underline != 0) + SSPRINTF (seq, &n, sz, tty->TS_enter_underline_mode, NULL); + /* Note: when face->tty_reverse_p != 0 and fg and bg are specified, + their values are already swapped and reversing them here would swap + them back, but we need to handle the reversal when unspecified. */ + if (face->tty_reverse_p && DEFAULTP (fg) && DEFAULTP (bg)) + SSPRINTF (seq, &n, sz, tty->TS_enter_reverse_mode, NULL); + + if (DEFAULTP (fg)) fg = fg_normal; + if (DEFAULTP (bg)) bg = bg_normal; + + const char *set_fg = tty->TS_set_foreground; + const char *set_bg = tty->TS_set_background; + if (tty->TN_max_colors == 8 || tty->TN_max_colors == 16) + { + /* fg and bg are indices into 16 base colors (see link at top). */ + unsigned long fgi = 0, bgi = 0; + + fgi = (fg >= 0 && fg < 8) + ? fg + 30 + : (fg >= 8 && fg < 16) + ? fg - 8 + 90 + : 0; + if (fgi) + SSPRINTF (seq, &n, sz, set_fg, fgi); + + bgi = (bg >= 0 && bg < 8) + ? bg + 40 + : (bg >= 8 && bg < 16) + ? bg - 8 + 100 + : 0; + if (bgi) + SSPRINTF (seq, &n, sz, set_bg, bgi); + } + else if (tty->TN_max_colors == 256) + { + /* fg and bg are xterm indices. */ + if (fg >= 0 && fg < 256) + SSPRINTF (seq, &n, sz, set_fg, fg); + + if (bg >= 0 && bg < 256) + SSPRINTF (seq, &n, sz, set_bg, bg); + } + else if (tty->TN_max_colors == 16777216) + { + /* fg and bg are pixel values -- decompose to rgb triples. */ + unsigned long rf = fg/65536, gf = (fg/256)&255, bf = fg&255; + unsigned long rb = bg/65536, gb = (bg/256)&255, bb = bg&255; + SSPRINTF (seq, &n, sz, set_fg, rf, gf, bf); + SSPRINTF (seq, &n, sz, set_bg, rb, gb, bb); + } + w32con_write_vt_seq ((const char *) seq); +} + +static void +turn_off_face (struct frame *f, int face_id) +{ + struct tty_display_info *tty = FRAME_TTY (f); + w32con_write_vt_seq (tty->TS_exit_attribute_mode); +} + /* The IME window is needed to receive the session notifications required to reset the low level keyboard hook state. */ @@ -868,6 +1048,8 @@ initialize_w32_display (struct terminal *term, int *width, int *height) } char_attr_normal = info.wAttributes; + fg_normal = char_attr_normal & 0x000f; + bg_normal = (char_attr_normal >> 4) & 0x000f; /* Determine if the info returned by GetConsoleScreenBufferInfo is realistic. Old MS Telnet servers used to only fill out @@ -921,31 +1103,81 @@ initialize_w32_display (struct terminal *term, int *width, int *height) /* Set up the keyboard hook. */ setup_w32_kbdhook (hwnd); + + /* Set current_tty to the tty of this terminal */ + current_tty = term->display_info.tty; } -DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 2, 0, +DEFUN ("set-screen-color", Fset_screen_color, Sset_screen_color, 2, 3, 0, doc: /* Set screen foreground and background colors. -Arguments should be indices between 0 and 15, see w32console.el. */) - (Lisp_Object foreground, Lisp_Object background) +Arguments should be indices for colors in the list returned by `tty-color-alist'. +If VTP is non-nil, settings affect virtual terminal processing only. +Otherwise, arguments should be between 0 and 15, and settings will +be effective only when virtual terminal processing is disabled. + +See w32console.el and the documentation for `w32-use-virtual-terminal'. */) + (Lisp_Object foreground, Lisp_Object background, Lisp_Object vtp) { - char_attr_normal = XFIXNAT (foreground) + (XFIXNAT (background) << 4); + int fg = XFIXNAT (foreground); + int bg = XFIXNAT (background); + + if (NILP (vtp)) + { + char_attr_normal = fg + (bg << 4); + } + else + { + fg_normal = fg; + bg_normal = bg; + } Frecenter (Qnil, Qt); return Qt; } -DEFUN ("get-screen-color", Fget_screen_color, Sget_screen_color, 0, 0, 0, +DEFUN ("get-screen-color", Fget_screen_color, Sget_screen_color, 0, 1, 0, doc: /* Get color indices of the current screen foreground and background. -The colors are returned as a list of 2 indices (FOREGROUND BACKGROUND). -See w32console.el and `tty-defined-color-alist' for mapping of indices -to colors. */) +The colors are returned as a list of 2 indices (FOREGROUND BACKGROUND) for +colors in the list returned by `tty-color-alist`. + +If VTP is non-nil, returns settings effective when virtual terminal +processing is enabled. Otherwise, returns settings effective when +virtual terminal processing is disabled. + +See w32console.el and the documentation for `w32-use-virtual-terminal'. */) + (Lisp_Object vtp) +{ + int fg = NILP (vtp) ? char_attr_normal & 0x000f : fg_normal; + int bg = NILP (vtp) ? (char_attr_normal >> 4) & 0x000f : bg_normal; + + return Fcons (make_fixnum (fg), Fcons (make_fixnum (bg), Qnil)); +} + +DEFUN ("w32-use-virtual-terminal", Fw32_use_virtual_terminal, Sw32_use_virtual_terminal, 1, 1, 0, + doc: /* Enables (disables) virtual terminal sequence processing if argument is t (nil). */) + (Lisp_Object arg) +{ + if (EQ (arg, Qt)) + w32_use_virtual_terminal = 1; + else if (EQ (arg, Qnil)) + w32_use_virtual_terminal = 0; + else { + error ("Invalid argument: expects t or nil."); + return Qnil; + } + + w32con_setup_virtual_terminal (); + return Qt; +} + +DEFUN ("w32-use-virtual-terminal-p", Fw32_use_virtual_terminal_p, Sw32_use_virtual_terminal_p, 0, 0, 0, + doc: /* Returns t (nil) if virtual terminal sequence processing is enabled (disabled). */) (void) { - return Fcons (make_fixnum (char_attr_normal & 0x000f), - Fcons (make_fixnum ((char_attr_normal >> 4) & 0x000f), Qnil)); + return w32_use_virtual_terminal ? Qt : Qnil; } DEFUN ("set-cursor-size", Fset_cursor_size, Sset_cursor_size, 1, 1, 0, @@ -974,5 +1206,7 @@ scroll-back buffer. */); defsubr (&Sset_screen_color); defsubr (&Sget_screen_color); + defsubr (&Sw32_use_virtual_terminal); + defsubr (&Sw32_use_virtual_terminal_p); defsubr (&Sset_cursor_size); } diff --git a/src/xdisp.c b/src/xdisp.c index 6f06206b43e..99474d7abdc 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -17387,7 +17387,7 @@ redisplay_internal (void) area, displaying a different frame means redisplay the whole thing. */ SET_FRAME_GARBAGED (sf); -#if !defined DOS_NT && !defined HAVE_ANDROID +#if !defined MSDOS && !defined HAVE_ANDROID set_tty_color_mode (FRAME_TTY (sf), sf); #endif }