Support 24-bit TrueColor on MS-Windows console

* src/w32console.c (DEFAULTP, SSPRINTF, w32con_write_vt_seq)
(w32con_get_cursor_coords): New functions and macros.
(w32con_write_glyphs): Hide cursor before writing to the console.
Add code for writing in virtual-terminal mode when
'w32_use_virtual_terminal' is non-zero.
(w32con_write_glyphs_with_face): Add code for writing in
virtual-terminal mode when 'w32_use_virtual_terminal' is non-zero.
(w32con_setup_virtual_terminal): New function.
(w32con_set_terminal_modes): Call it.
(turn_on_face, turn_off_face): New functions.
(initialize_w32_display): Save background and foreground, and the
current TTY.
(Fset_screen_color): Accept an additional optional argument VTP;
if non-nil, arrange for 24-bit display to use the specified
colors.
(Fget_screen_color): Accept an additional optional argument VTP;
if non-nil, return colors used by 24-bit display.
(Fw32_use_virtual_terminal, Fw32_use_virtual_terminal_p): New
functions.
* src/term.c (tty_setup_colors) [WINDOWSNT]: Set up
virtual-terminal sequences for colors.
(init_tty) [WINDOWSNT]: Set up terminfo capabilities for Windows
virtual-terminal.
* src/xdisp.c (redisplay_internal): Don't call set_tty_color_mode
for WINDOWSNT.

* lisp/term/w32console.el (w32-tty-set-base-colors)
(w32-tty-define-base-colors, w32-tty-define-8bit-colors)
(w32-tty-define-24bit-colors, w32-tty-get-pixel): New functions.
(terminal-init-w32console): Remove color setup.
(w32-tty-setup-colors): New function.
* lisp/term/tty-colors.el (tty-color-mode-alist): Add --color
modes for 256 and 24-bit color modes.
* lisp/startup.el (tty-handle-args): Fix --color handling.
* lisp/faces.el (tty-set-up-initial-frame-faces): Set up colors
for MS-Windows consoles.

* etc/NEWS:
* doc/emacs/msdos.texi (Windows Misc):
* doc/emacs/cmdargs.texi (Colors X): Document 24-bit color support
on MS-Windows.
(Bug#79298)
This commit is contained in:
ewantown
2025-10-12 07:23:53 -07:00
committed by Eli Zaretskii
parent 9b8361d9ab
commit 2bca4ac0ed
11 changed files with 515 additions and 110 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -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))

View File

@@ -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)

View File

@@ -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

View File

@@ -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;

View File

@@ -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 */

View File

@@ -19,6 +19,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
/*
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);
}

View File

@@ -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
}