控制台虚拟终端序列

虚拟终端序列是控制字符序列,可在写入输出流时控制游标移动、控制台颜色和其他作。 还可以在输入流上接收序列,以响应输出流查询信息序列,或者在设置适当的模式时作为用户输入的编码。

可以使用 GetConsoleModeSetConsoleMode 函数来配置此行为。 本文档末尾包含了启用虚拟终端行为的建议方法示例。

以下序列的行为基于 VT100 和派生的终端模拟器技术,尤其是 xterm 终端模拟器。 有关终端序列的详细信息,请参阅 at .

输出序列

当写入输出流时,控制台主机会截获以下终端序列,如果使用 SetConsoleMode 函数在屏幕缓冲区句柄上设置ENABLE_VIRTUAL_TERMINAL_PROCESSING标志。 请注意,DISABLE_NEWLINE_AUTO_RETURN标志还可用于模拟其他终端模拟器的游标定位和滚动行为,这与写入到任何行中最后列的字符有关。

简单光标定位

在以下所有说明中,ESC 始终是十六进制值0x1B。 终端序列中不包含空格。 单个终端序列可以在任意字符或字节位置拆分,跨对 WriteFileWriteConsole 的多个顺序调用,但最佳做法是在一次调用中包含整个序列。 有关实际使用这些序列的示例,请参阅本主题末尾的示例。

下表描述了在 ESC 字符之后直接使用单个作命令的简单转义序列。 这些序列没有参数,并立即生效。

此表中的所有命令通常等效于调用 SetConsoleCursorPosition 控制台 API 来放置游标。

光标移动将由当前视区绑定到缓冲区。 不会滚动(如果可用)。

序列 速记 行为
ESC M RI 反向索引 – 执行 \n的反向作,将光标向上移动一行,维护水平位置,如有必要,滚动缓冲区*
ESC 7 DECSC 将光标位置保存在内存中**
ESC 8 DECSR 从内存还原光标位置**

注释

* 如果设置了滚动边距,边距内的 RI 将仅滚动边距的内容,并使视区保持不变。 (请参阅滚动边距)

**在首次使用 save 命令之前,内存中不会保存任何值。 访问已保存值的唯一方法是使用还原命令。

光标定位

下表包含控制序列引入器(CSI)类型序列。 所有 CSI 序列以 ESC(0x1B)开头,后跟 [(左括号,0x5B),并可能包含可变长度的参数,以指定每个作的详细信息。 这将用简写 <n> 表示。 下表按功能分组,每个表下方都有说明说明组的工作原理。

对于所有参数,除非另有说明,否则以下规则适用:

  • <n> 表示要移动的距离,并且是一个可选参数
  • 如果 <省略 n> 或等于 0,它将被视为 1
  • <n> 不能大于 32,767 (最大短值)
  • <n> 不能为负

本节中的所有命令通常等效于调用 SetConsoleCursorPosition 控制台 API。

光标移动将由当前视区绑定到缓冲区。 不会滚动(如果可用)。

序列 Code DESCRIPTION 行为
ESC [ <n> A CUU 向上游标 向上游标 n <>
ESC [ <n> B 反刍物 光标向下 按 n 向下 <光标>
ESC [ <n> C CUF 光标向前 光标向前 (右) 按 <n>
ESC [ <n> D CUB 向后光标 光标向后(左)按 <n>
ESC [ <n> E CNL 光标下一行 从当前位置向下 <游标 n> 行
ESC [ <n> F CPL 光标上一行 从当前位置向上 <游标 n> 行
ESC [ <n> G 光标水平绝对值 光标在当前行中水平移动到 <第 n>个位置
ESC [ <n> d VPA 垂直线位置绝对 光标在当前列中垂直移动到 <第 n>个位置
ESC [ <y> ; <x> H 杯子 光标位置 *光标移动到 <x>; <视区中的 y> 坐标,其中 <x> 是 y> 线的<列
ESC [ <y> ; <x> f HVP 水平垂直位置 *光标移动到 <x>; <视区中的 y> 坐标,其中 <x> 是 y> 线的<列
ESC [ s ANSISYSSC 保存游标 - Ansi.sys 仿真 **如果没有参数,则执行像 DECSC 这样的保存游标作
ESC [ u ANSISYSRC 还原游标 - Ansi.sys 仿真 **如果没有参数,则执行 DECRC 等还原游标作

注释

*<x> 和 <y> 参数的限制与上述 n> 相同<。 如果 <省略 x> 和 <y> ,它们将设置为 1;1。

** 可以在ANSI.sys 历史文档中找到 https://msdn.microsoft.com/library/cc722862.aspx 并实现方便/兼容性。

游标可见性

以下命令控制光标及其闪烁状态的可见性。 DECTCEM 序列通常等效于调用 SetConsoleCursorInfo 控制台 API 来切换游标可见性。

序列 Code DESCRIPTION 行为
ESC [ ? 12 小时 ATT160 文本光标启用闪烁 启动光标闪烁
ESC [ ? 12 l ATT160 文本光标禁用闪烁 停止闪烁光标
ESC [ ? 25 小时 DECTCEM 文本光标启用模式显示 显示光标
ESC [ ? 25 l DECTCEM 文本光标启用模式隐藏 隐藏光标

小窍门

启用序列以小写 H 字符(h)结尾,禁用序列以小写 L 字符结尾(l)。

光标形状

以下命令控制并允许自定义光标形状。

序列 Code DESCRIPTION 行为
ESC [ 0 SP q DECSCUSR 用户形状 用户配置的默认光标形状
ESC [ 1 SP q DECSCUSR 闪烁块 闪烁块光标形状
ESC [ 2 SP q DECSCUSR 稳定块 稳定块光标形状
ESC [ 3 SP q DECSCUSR 闪烁下划线 闪烁下划线光标形状
ESC [ 4 SP q DECSCUSR 稳定下划线 稳定下划线光标形状
ESC [ 5 SP q DECSCUSR 闪烁栏 闪烁条光标形状
ESC [ 6 SP q DECSCUSR 稳定条形图 稳定条形光标形状

注释

SP 是中间位置的文本空格字符(0x20),后跟 q 最后一个位置(0x71)。

视区定位

本节中的所有命令通常等效于调用 ScrollConsoleScreenBuffer 控制台 API 来移动控制台缓冲区的内容。

谨慎 命令名称具有误导性。 滚动是指文本在作期间移动的方向,而不是视区似乎移动的哪个方向。

序列 Code DESCRIPTION 行为
ESC [ <n> S SU 向上滚动 向上滚动文本 n<>。 也称为向下平移,新线条从屏幕底部填充
ESC [ <n> T SD(标准清晰度) 向下滚动 向下滚动 <n>。 也称为向上平移,新行从屏幕顶部填充

文本从光标位于的行开始移动。 如果光标位于视区中间行上,则向上滚动将移动视区底部的下半部分,并在底部插入空白行。 向下滚动将移动视区行的上半部分,并在顶部插入新行。

另一个重要事项是向上和向下滚动也会受到滚动边距的影响。 向上和向下滚动不会影响滚动边距之外的任何行。

n> 的<默认值为 1,并且可以选择性地省略该值。

文本修改

本节中的所有命令通常等效于调用 FillConsoleOutputCharacterFillConsoleOutputAttributeScrollConsoleScreenBuffer 控制台 API 以修改文本缓冲区内容。

序列 Code DESCRIPTION 行为
ESC [ <n> @ ICH 插入字符 在当前光标位置插入 <n 个> 空格,并将所有现有文本向右移动。 将删除屏幕右侧退出的文本。
ESC [ <n> P DCH 删除字符 在当前光标位置删除 <n> 个字符,从屏幕右边缘移入空格字符。
ESC [ <n> X ECH 擦除字符 使用 <空格字符覆盖当前光标位置中的 n> 个字符。
ESC [ <n> L IL 插入行 将 <n> 行插入缓冲区中的光标位置。 光标位于的行,其下方的行将向下移动。
ESC [ <n> M DL 删除行 <从缓冲区中删除 n> 行,从光标位于的行开始。

注释

对于 IL 和 DL,仅影响滚动边距中的行(请参阅滚动边距)。 如果未设置边距,则默认边距边框为当前视区。 如果行将移入边距下方,则会将其丢弃。 删除行时,空白行将插入边距底部,视区外部的行永远不会受到影响。

对于每个序列,如果省略 n> 个序列,<则默认值为 0。

对于以下命令,参数 <n> 具有 3 个有效值:

  • 0 从当前光标位置(含)到行/显示末尾的擦除
  • 1 从行/显示开始处擦除,包括当前光标位置
  • 2 擦除整行/显示
序列 Code DESCRIPTION 行为
ESC [ <n> J ED 在显示中擦除 将当前视区/屏幕<>中指定的所有文本替换为空格字符
ESC [ <n> K EL 行中擦除 将行中的所有文本替换为用<>空格字符指定的光标

文本格式

本节中的所有命令通常等效于调用 SetConsoleTextAttribute 控制台 API,以调整所有将来写入控制台输出文本缓冲区的格式。

此命令很特别,因此 <下面的 n> 个位置可以接受 0 到 16 个用分号分隔的参数。

如果未指定任何参数,则会将其视为与单个 0 参数相同的参数。

序列 Code DESCRIPTION 行为
ESC [ <n> m SGR 设置图形呈现形式 设置由 n 指定的 <屏幕和文本的格式>

下表中的值可用于 <n> 表示不同的格式模式。

格式设置模式从左到右应用。 应用竞争格式设置选项将导致优先选择最正确的选项。

对于指定颜色的选项,颜色将用作控制台颜色表中的定义,可以使用 SetConsoleScreenBufferInfoEx API 对其进行修改。 如果修改表以使表中的“蓝色”位置显示红色的 RGB 底纹,则对 前台蓝色 的所有调用都将显示红色,直到其他更改为止。

价值 DESCRIPTION 行为
0 违约 在修改之前返回默认状态的所有属性
1 粗体/亮 将亮度/强度标志应用于前景色
22 无粗体/亮 从前景色中删除亮度/强度标志
4 下划线 添加下划线
二十四 无下划线 删除下划线
7 消极 交换前景和背景色
二十七 正 (无负) 返回前台/背景为正常
30 前景色黑色 将非粗体/亮黑色应用于前台
31 前景红色 将非粗体/亮红色应用于前台
32 前景绿色 将非粗体/亮绿色应用于前台
33 前景黄色 将非粗体/亮黄色应用于前台
34 前景蓝色 将非粗体/亮蓝色应用于前台
35 前景 Magenta 将非粗体/明亮的洋红应用于前台
36 前景 Cyan 将非粗体/亮青色应用于前台
37 前景白色 将非粗体/亮白色应用于前台
三十八 前台扩展 将扩展的颜色值应用于前台(请参阅下面的详细信息)
39 前台默认值 仅应用默认值的前景部分(请参阅 0)
40 背景黑色 将非粗体/亮黑色应用于背景
41 背景红色 将非粗体/亮红色应用于背景
42 背景绿色 将非粗体/亮绿色应用于背景
43 背景黄色 将非粗体/亮黄色应用于背景
44 背景蓝色 将非粗体/亮蓝色应用于背景
45 背景红 将非粗体/明亮的洋红应用于背景
46 背景青色 将非粗体/亮青色应用于背景
47 背景白色 将非粗体/亮白色应用于背景
48 后台扩展 将扩展的颜色值应用于背景(请参阅下面的详细信息)
49 背景默认值 仅应用默认值的背景部分(请参阅 0)
90 亮前景黑色 将粗体/明亮的黑色应用于前台
91 亮前景红色 将粗体/亮红色应用于前台
92 明亮的前景绿色 将粗体/亮绿色应用于前台
93 亮前景黄色 将粗体/亮黄色应用于前台
94 亮前景蓝色 将粗体/亮蓝色应用于前台
95 光明前景 Magenta 将粗体/明亮的洋红应用于前台
96 Bright Foreground Cyan 将粗体/明亮的青绿色应用于前台
97 明亮的前景白色 将粗体/明亮的白色应用于前台
100 亮背景黑色 将粗体/明亮的黑色应用于背景
101 亮背景红色 将粗体/亮红色应用于背景
102 亮背景绿色 将粗体/亮绿色应用于背景
103 亮背景黄色 将粗体/亮黄色应用于背景
104 亮背景蓝色 将粗体/亮蓝色应用于背景
105 亮背景红 将粗体/明亮的洋红应用于背景
106 亮背景青色 将粗体/明亮的青色应用于背景
107 亮背景白色 将粗体/亮白色应用于背景

扩展颜色

某些虚拟终端模拟器支持大于 Windows 控制台提供的 16 种颜色的调色板。 对于这些扩展颜色,Windows 控制台将从现有的 16 种颜色表中选择最接近的相应颜色进行显示。 与上面的典型 SGR 值不同,扩展值将在初始指示器后根据下表使用其他参数。

SGR 子序列 DESCRIPTION
38 ;2 ; <r> ; <g> ; <b> 将前景色设置为 r>、g>、<<b> 参数中指定的 <RGB 值*
48 ;2 ; <r> ; <g> ; <b> 将背景色设置为 r>、<g>、<b> 参数中指定的 <RGB 值*
38 ;5 ; <s> 将前景色设置为 <88 或 256 颜色表中的> 索引*
48 ;5 ; <s> 在 88 或 256 颜色表中将背景色设置为 <索引> *

*为了进行比较,内部维护的 88 和 256 调色板基于 xterm 终端模拟器。 目前无法修改比较/舍入表。

屏幕颜色

以下命令允许应用程序将屏幕调色板值设置为任何 RGB 值。

RGB 值应是两0ff者之间的十六进制值,并由正斜杠字符(例如)rgb:1/24/86分隔。

请注意,此序列是 OSC“作系统命令”序列,而不是与列出的许多其他序列一样,而不是 CSI,例如,从“\x1b]”开始,而不是“\x1b[”。 作为 OSC 序列,它们以一个字符串终止符结束,该终止符表示<ST>并用 (0x1B 0x5C) 进行传输ESC \BEL0x7) 可以改用为终止符,但首选较长的形式。

序列 DESCRIPTION 行为
ESC ] 4; <i> ;rgb : <r> / <g> / <b><ST> 修改屏幕颜色 将屏幕调色板索引 <i> 设置为 r>、 g>, <<b 中指定的 <RGB 值>

模式更改

这些是控制输入模式的序列。 有两组不同的输入模式:光标键模式和键盘键模式。 光标键模式控制箭头键以及主页和结束发出的序列,而键盘键模式主要控制数字板上的键发出的序列以及函数键。

其中每个模式都是简单的布尔设置 - 光标键模式为“普通”(默认值)或“应用程序”,键盘键模式为“数字”(默认值)或“应用程序”。

有关在这些模式下发出的序列,请参阅游标键和 Numpad 和函数键部分。

序列 Code DESCRIPTION 行为
ESC = DECKPAM 启用键盘应用程序模式 键盘键将发出其应用程序模式序列。
ESC > DECKPNM 启用键盘数字模式 键盘键将发出其数字模式序列。
ESC [ ? 1 小时 DECCKM 启用游标键应用程序模式 键盘键将发出其应用程序模式序列。
ESC [ ? 1 l DECCKM 禁用游标键应用程序模式(使用正常模式) 键盘键将发出其数字模式序列。

查询状态

本节中的所有命令通常等效于调用 Get* 控制台 API 以检索有关当前控制台缓冲区状态的状态信息。

注释

这些查询会在设置ENABLE_VIRTUAL_TERMINAL_PROCESSING时在输出流上识别后立即将其响应发出到控制台输入流中。 ENABLE_VIRTUAL_TERMINAL_INPUT标志不适用于查询命令,因为假定发出查询的应用程序将始终希望接收答复。

序列 Code DESCRIPTION 行为
ESC [ 6 n DECXCPR 报表游标位置 将光标位置发出为:ESC [ <r> ; <c> R Where <r> = cursor row and <c> = cursor column
ESC [ 0 c DA 设备属性 报告终端标识。 将发出“\x1b[?1;0c“,指示”VT101 无选项”。

制表符

虽然 Windows 控制台传统上希望选项卡宽为 8 个字符,但 *nix 应用程序利用某些序列可以作制表位在控制台窗口中的位置,以优化应用程序的游标移动。

以下序列允许应用程序设置控制台窗口中的制表位位置、删除它们并在它们之间导航。

序列 Code DESCRIPTION 行为
ESC H 高温 超导 水平选项卡集 在光标位于的当前列中设置制表位。
ESC [ <n> I CHT 光标水平 (向前) 选项卡 使用制表位将光标前进到下一列(在同一行中)。 如果没有更多制表位,请移动到行中的最后一列。 如果光标位于最后一列中,则移动到下一行的第一列。
ESC [ <n> Z CBT “向后光标”选项卡 使用制表位将光标移动到上一列(在同一行中)。 如果没有更多制表位,将光标移动到第一列。 如果游标位于第一列中,则不移动游标。
ESC [ 0 g TBC Tab Clear (当前列) 清除当前列中的制表位(如果有)。 否则不执行任何作。
ESC [ 3 g TBC Tab 清除(所有列) 清除当前设置的所有制表位。
  • 对于 CHT 和 CBT,n <> 是一个可选参数(default=1),指示将光标前进到指定方向的次数。
  • 如果没有通过 HTS 设置制表位,CHT 和 CBT 会将窗口的第一列和最后一列视为唯一的两个制表位。
  • 使用 HTS 设置制表位也会导致控制台以与 CHT 相同的方式导航到 TAB (0x09, '\t' 字符)输出上的下一个制表位。

指定字符集

以下序列允许程序更改活动字符集映射。 这样,程序就可以发出 7 位 ASCII 字符,但将它们显示为终端屏幕上的其他字形。 目前,唯一支持的两个字符集是 ASCII(默认值)和 DEC 特殊图形字符集。 有关 DEC 特殊图形字符集所表示的所有字符的列表,请参阅 http://vt100.net/docs/vt220-rm/table2-4.html

序列 DESCRIPTION 行为
ESC (0) 指定字符集 – DEC 线条绘制 启用 DEC 线条绘制模式
ESC (B 指定字符集 – US ASCII 启用 ASCII 模式(默认)

值得注意的是,DEC 线条绘制模式用于在控制台应用程序中绘制边框。 下表显示了 ASCII 字符映射到哪些线条绘制字符。

Hex ASCII DEC 线条绘制
0x6a j
0x6b k
0x6c l
0x6d m
0x6e n
0x71 q
0x74 t
0x75 u
0x76 v
0x77 w
0x78 x

滚动边距

以下序列允许程序配置受滚动作影响的屏幕的“滚动区域”。 这是当屏幕将滚动时调整的行的子集,例如,在“\n”或 RI 上滚动。 这些边距还影响插入行(IL)和删除行(DL)、向上滚动(SU)和向下滚动(SD)修改的行。

滚动边距对于在填充屏幕其余部分时没有滚动的屏幕(例如顶部有标题栏或应用程序底部的状态栏)特别有用。

对于 DECSTBM,有两个可选参数 <t> 和 <b>,用于指定表示滚动区域的上行和下行(含)。 如果省略参数,<则 t> 默认为 1,b <> 默认为当前视区高度。

滚动边距是每个缓冲区,因此重要的是,备用缓冲区和主缓冲区保持单独的滚动边距设置(因此备用缓冲区中的全屏应用程序不会毒害主缓冲区的边距)。

序列 Code DESCRIPTION 行为
ESC [ <t> ; <b> r DECSTBM 设置滚动区域 设置视区 VT 滚动边距。

窗口标题

以下命令允许应用程序将控制台窗口的标题设置为给定 <的字符串> 参数。 字符串必须小于 255 个字符才能接受。 这相当于使用给定字符串调用 SetConsoleTitle。

请注意,这些序列是 OSC“作系统命令”序列,而不是 CSI,就像列出的许多其他序列一样,因此从“\x1b]”开始,而不是“\x1b[”。 作为 OSC 序列,它们以一个字符串终止符结束,该终止符表示<ST>并用 (0x1B 0x5C) 进行传输ESC \BEL0x7) 可以改用为终止符,但首选较长的形式。

序列 DESCRIPTION 行为
ESC ] 0 ; <字符串><圣> 设置窗口标题 将控制台窗口的标题设置为 <字符串>。
ESC ] 2; <字符串><圣> 设置窗口标题 将控制台窗口的标题设置为 <字符串>。

此处的终止字符是“Bell”字符“\x07”

备用屏幕缓冲区

*Nix 样式应用程序通常利用备用屏幕缓冲区,以便它们可以修改缓冲区的全部内容,而不会影响启动它们的应用程序。 备用缓冲区正好是窗口的尺寸,没有任何回滚区域。

有关此行为的示例,请考虑何时从 bash 启动 vim。 Vim 使用整个屏幕来编辑文件,然后返回到 bash,使原始缓冲区保持不变。

序列 DESCRIPTION 行为
ESC [ ? 1 0 4 9 小时 使用备用屏幕缓冲区 切换到新的备用屏幕缓冲区。
ESC [ ? 1 0 4 9 l 使用主屏幕缓冲区 切换到主缓冲区。

窗口宽度

以下序列可用于控制控制台窗口的宽度。 它们大致相当于调用 SetConsoleScreenBufferInfoEx 控制台 API 来设置窗口宽度。

序列 Code DESCRIPTION 行为
ESC [ ? 3 小时 DECCOLM 将列数设置为 132 将控制台宽度设置为宽 132 列。
ESC [ ? 3 l DECCOLM 将列数设置为 80 将控制台宽度设置为宽 80 列。

软重置

以下序列可用于将某些属性重置为其默认值。以下属性将重置为以下默认值(还列出了控制这些属性的序列):

  • 游标可见性:可见(DECTEM)
  • 数字键盘:数字模式(DECNKM)
  • 光标键模式:正常模式(DECCKM)
  • 上边距和底部边距:Top=1,Bottom=Console 高度(DECSTBM)
  • 字符集:US ASCII
  • 图形呈现形式:默认/关闭(SGR)
  • 保存光标状态:主页位置 (0,0) (DECSC)
序列 Code DESCRIPTION 行为
ESC [ ! p DECSTR 软重置 将某些终端设置重置为其默认值。

输入序列

如果使用 SetConsoleMode 标志在输入缓冲区句柄上设置ENABLE_VIRTUAL_TERMINAL_INPUT标志,则控制台主机上会发出以下终端序列。

有两种内部模式可以控制为给定输入键、光标键模式和键盘键模式发出哪些序列。 这些内容在“模式更改”部分中进行了介绍。

游标键

密钥 正常模式 应用程序模式
向上键 ESC [ A ESC O A
向下键 ESC [ B ESC O B
向右键 ESC [ C ESC O C
向左键 ESC [ D ESC O D
ESC [ H ESC O H
结束 ESC [ F ESC O F

此外,如果按 Ctrl 并使用以下任一键,则会发出以下序列,而不考虑光标键模式:

密钥 任何模式
Ctrl + 向上键 ESC [ 1 ; 5 A
Ctrl + 向下键 ESC [ 1 ; 5 B
Ctrl + 向右键 ESC [ 1 ; 5 C
Ctrl + 向左键 ESC [ 1 ; 5 D

Numpad 和函数键

密钥 序列
退格键 0x7f (DEL)
暂停 0x1a (SUB)
逃跑 0x1b (ESC)
插入 ESC [ 2 ~
删除 ESC [ 3 ~
向上翻页 ESC [ 5 ~
向下翻页 ESC [ 6 ~
一级方程式 ESC O P
F2 ESC O Q
F3 ESC O R
F4 ESC O S
F5 ESC [ 1 5 ~
F6 ESC [ 1 7 ~
F7 ESC [ 1 8 ~
F8 ESC [ 1 9 ~
F9 ESC [ 2 0 ~
F10 ESC [ 2 1 ~
F11 ESC [ 2 3 ~
F12 ESC [ 2 4 ~

修饰 符

Alt 通过为序列添加转义前缀来处理 Alt:ESC <c> ,其中 <c> 是作系统传递的字符。 Alt+Ctrl 的处理方式与作系统将 c> 键预先转移到<要中继到应用程序的相应控制字符相同。

Ctrl 通常与从系统接收的完全一样传递。 这通常是单个字符向下移入控制字符保留空间(0x0-0x1f)。 例如,Ctrl+@ (0x40) 变为 NUL (0x00),Ctrl+[ (0x5b) 变为 ESC(0x1b),等等。根据下表专门处理几个 Ctrl 键组合:

密钥 序列
Ctrl + 空格 0x00 (NUL)
Ctrl + 向上键 ESC [ 1 ; 5 A
Ctrl + 向下键 ESC [ 1 ; 5 B
Ctrl + 向右键 ESC [ 1 ; 5 C
Ctrl + 向左键 ESC [ 1 ; 5 D

注释

Ctrl + 向右 Alt 被视为 AltGr。 当两者一起看到时,它们将被剥离,系统呈现的字符的 Unicode 值将传递到目标中。 系统将根据当前的系统输入设置预先转换 AltGr 值。

样品

SGR 终端序列的示例

以下代码提供了多个文本格式示例。

#include <stdio.h>
#include <wchar.h>
#include <windows.h>

int main()
{
    // Set output mode to handle virtual terminal sequences
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hOut == INVALID_HANDLE_VALUE)
    {
        return GetLastError();
    }

    DWORD dwMode = 0;
    if (!GetConsoleMode(hOut, &dwMode))
    {
        return GetLastError();
    }

    dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    if (!SetConsoleMode(hOut, dwMode))
    {
        return GetLastError();
    }

    // Try some Set Graphics Rendition (SGR) terminal escape sequences
    wprintf(L"\x1b[31mThis text has a red foreground using SGR.31.\r\n");
    wprintf(L"\x1b[1mThis text has a bright (bold) red foreground using SGR.1 to affect the previous color setting.\r\n");
    wprintf(L"\x1b[mThis text has returned to default colors using SGR.0 implicitly.\r\n");
    wprintf(L"\x1b[34;46mThis text shows the foreground and background change at the same time.\r\n");
    wprintf(L"\x1b[0mThis text has returned to default colors using SGR.0 explicitly.\r\n");
    wprintf(L"\x1b[31;32;33;34;35;36;101;102;103;104;105;106;107mThis text attempts to apply many colors in the same command. Note the colors are applied from left to right so only the right-most option of foreground cyan (SGR.36) and background bright white (SGR.107) is effective.\r\n");
    wprintf(L"\x1b[39mThis text has restored the foreground color only.\r\n");
    wprintf(L"\x1b[49mThis text has restored the background color only.\r\n");

    return 0;
}

注释

在前面的示例中,字符串“\x1b[31m”是 ESC [ <n m 的实现,n>> 为 31。<

下图显示了上一个代码示例的输出。

使用 sgr 命令输出控制台

启用虚拟终端处理的示例

以下代码提供了为应用程序启用虚拟终端处理的建议方法的示例。 示例的意图是演示:

  1. 在设置 SetConsoleMode 之前,应始终通过 GetConsoleMode 检索现有模式并对其进行分析。

  2. 检查 SetConsoleMode 是否返回 0 且 GetLastError 返回ERROR_INVALID_PARAMETER是确定在下层系统上运行时的当前机制。 接收ERROR_INVALID_PARAMETER且位字段中具有较新的控制台模式标志之一的应用程序应正常降级行为,然后重试。

#include <stdio.h>
#include <wchar.h>
#include <windows.h>

int main()
{
    // Set output mode to handle virtual terminal sequences
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hOut == INVALID_HANDLE_VALUE)
    {
        return false;
    }
    HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
    if (hIn == INVALID_HANDLE_VALUE)
    {
        return false;
    }

    DWORD dwOriginalOutMode = 0;
    DWORD dwOriginalInMode = 0;
    if (!GetConsoleMode(hOut, &dwOriginalOutMode))
    {
        return false;
    }
    if (!GetConsoleMode(hIn, &dwOriginalInMode))
    {
        return false;
    }

    DWORD dwRequestedOutModes = ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
    DWORD dwRequestedInModes = ENABLE_VIRTUAL_TERMINAL_INPUT;

    DWORD dwOutMode = dwOriginalOutMode | dwRequestedOutModes;
    if (!SetConsoleMode(hOut, dwOutMode))
    {
        // we failed to set both modes, try to step down mode gracefully.
        dwRequestedOutModes = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        dwOutMode = dwOriginalOutMode | dwRequestedOutModes;
        if (!SetConsoleMode(hOut, dwOutMode))
        {
            // Failed to set any VT mode, can't do anything here.
            return -1;
        }
    }

    DWORD dwInMode = dwOriginalInMode | dwRequestedInModes;
    if (!SetConsoleMode(hIn, dwInMode))
    {
        // Failed to set VT input mode, can't do anything here.
        return -1;
    }

    return 0;
}

选择周年更新功能的示例

以下示例旨在成为使用各种转义序列作缓冲区的代码更可靠的示例,重点介绍 Windows 10 周年更新中添加的功能。

此示例使用备用屏幕缓冲区、作制表位、设置滚动边距和更改字符集。

// System headers
#include <windows.h>

// Standard library C-style
#include <wchar.h>
#include <stdlib.h>
#include <stdio.h>

#define ESC "\x1b"
#define CSI "\x1b["

bool EnableVTMode()
{
    // Set output mode to handle virtual terminal sequences
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hOut == INVALID_HANDLE_VALUE)
    {
        return false;
    }

    DWORD dwMode = 0;
    if (!GetConsoleMode(hOut, &dwMode))
    {
        return false;
    }

    dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    if (!SetConsoleMode(hOut, dwMode))
    {
        return false;
    }
    return true;
}

void PrintVerticalBorder()
{
    printf(ESC "(0"); // Enter Line drawing mode
    printf(CSI "104;93m"); // bright yellow on bright blue
    printf("x"); // in line drawing mode, \x78 -> \u2502 "Vertical Bar"
    printf(CSI "0m"); // restore color
    printf(ESC "(B"); // exit line drawing mode
}

void PrintHorizontalBorder(COORD const Size, bool fIsTop)
{
    printf(ESC "(0"); // Enter Line drawing mode
    printf(CSI "104;93m"); // Make the border bright yellow on bright blue
    printf(fIsTop ? "l" : "m"); // print left corner 

    for (int i = 1; i < Size.X - 1; i++)
        printf("q"); // in line drawing mode, \x71 -> \u2500 "HORIZONTAL SCAN LINE-5"

    printf(fIsTop ? "k" : "j"); // print right corner
    printf(CSI "0m");
    printf(ESC "(B"); // exit line drawing mode
}

void PrintStatusLine(const char* const pszMessage, COORD const Size)
{
    printf(CSI "%d;1H", Size.Y);
    printf(CSI "K"); // clear the line
    printf(pszMessage);
}

int __cdecl wmain(int argc, WCHAR* argv[])
{
    argc; // unused
    argv; // unused
    //First, enable VT mode
    bool fSuccess = EnableVTMode();
    if (!fSuccess)
    {
        printf("Unable to enter VT processing mode. Quitting.\n");
        return -1;
    }
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hOut == INVALID_HANDLE_VALUE)
    {
        printf("Couldn't get the console handle. Quitting.\n");
        return -1;
    }

    CONSOLE_SCREEN_BUFFER_INFO ScreenBufferInfo;
    GetConsoleScreenBufferInfo(hOut, &ScreenBufferInfo);
    COORD Size;
    Size.X = ScreenBufferInfo.srWindow.Right - ScreenBufferInfo.srWindow.Left + 1;
    Size.Y = ScreenBufferInfo.srWindow.Bottom - ScreenBufferInfo.srWindow.Top + 1;

    // Enter the alternate buffer
    printf(CSI "?1049h");

    // Clear screen, tab stops, set, stop at columns 16, 32
    printf(CSI "1;1H");
    printf(CSI "2J"); // Clear screen

    int iNumTabStops = 4; // (0, 20, 40, width)
    printf(CSI "3g"); // clear all tab stops
    printf(CSI "1;20H"); // Move to column 20
    printf(ESC "H"); // set a tab stop

    printf(CSI "1;40H"); // Move to column 40
    printf(ESC "H"); // set a tab stop

    // Set scrolling margins to 3, h-2
    printf(CSI "3;%dr", Size.Y - 2);
    int iNumLines = Size.Y - 4;

    printf(CSI "1;1H");
    printf(CSI "102;30m");
    printf("Windows 10 Anniversary Update - VT Example");
    printf(CSI "0m");

    // Print a top border - Yellow
    printf(CSI "2;1H");
    PrintHorizontalBorder(Size, true);

    // // Print a bottom border
    printf(CSI "%d;1H", Size.Y - 1);
    PrintHorizontalBorder(Size, false);

    wchar_t wch;

    // draw columns
    printf(CSI "3;1H");
    int line = 0;
    for (line = 0; line < iNumLines * iNumTabStops; line++)
    {
        PrintVerticalBorder();
        if (line + 1 != iNumLines * iNumTabStops) // don't advance to next line if this is the last line
            printf("\t"); // advance to next tab stop

    }

    PrintStatusLine("Press any key to see text printed between tab stops.", Size);
    wch = _getwch();

    // Fill columns with output
    printf(CSI "3;1H");
    for (line = 0; line < iNumLines; line++)
    {
        int tab = 0;
        for (tab = 0; tab < iNumTabStops - 1; tab++)
        {
            PrintVerticalBorder();
            printf("line=%d", line);
            printf("\t"); // advance to next tab stop
        }
        PrintVerticalBorder();// print border at right side
        if (line + 1 != iNumLines)
            printf("\t"); // advance to next tab stop, (on the next line)
    }

    PrintStatusLine("Press any key to demonstrate scroll margins", Size);
    wch = _getwch();

    printf(CSI "3;1H");
    for (line = 0; line < iNumLines * 2; line++)
    {
        printf(CSI "K"); // clear the line
        int tab = 0;
        for (tab = 0; tab < iNumTabStops - 1; tab++)
        {
            PrintVerticalBorder();
            printf("line=%d", line);
            printf("\t"); // advance to next tab stop
        }
        PrintVerticalBorder(); // print border at right side
        if (line + 1 != iNumLines * 2)
        {
            printf("\n"); //Advance to next line. If we're at the bottom of the margins, the text will scroll.
            printf("\r"); //return to first col in buffer
        }
    }

    PrintStatusLine("Press any key to exit", Size);
    wch = _getwch();

    // Exit the alternate buffer
    printf(CSI "?1049l");

}