我正在 Pascal 中为 Delphi 编写一个跨平台库,用于从终端进行写入和读取。
我使用
Winapi.Windows
单元的功能和一些 Linux 控制字符的组合来更改屏幕上回显的文本的背景和前景色。
我遇到一个问题,前一行的背景颜色以某种方式溢出到下一行,但本身没有溢出。
for var I := 1 to 6 do
begin
var TextAttrib: integer;
var Buff: TConsoleScreenBufferInfo;
const Handle = TTextRec(Output).Handle;
// Red
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(4) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
System.WriteLn('Debug test');
SetConsoleTextAttribute(Handle, $0F);
// Green
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(2) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
System.WriteLn('Color filler test');
SetConsoleTextAttribute(Handle, $0F);
// Blue
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(1) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
System.WriteLn('Line overflow');
SetConsoleTextAttribute(Handle, $0F);
System.WriteLn('');
end;
在 Windows 上,该问题仅在第 4 次左右的 echo 之后发生,但此代码的 Linux 等效版本始终存在相同的问题。无论如何,我的方法显然有问题。
那么,我做错了什么?
当我在一个简单的 Delphi 控制台应用程序中尝试你的代码时,我根本没有得到彩色背景。
调试时,我看到
Handle
的值为 0。这与 GetStdHandle(STD_OUTPUT_HANDLE)
返回的值不同。
因此,当您的代码调用
SetConsoleTextAttribute(Handle, TextAttrib);
时,它实际上是在尝试将文本属性设置为错误的句柄。
鉴于此,我修改了您的代码,以便它从一开始就正确检索控制台句柄
var Handle: THandle;
Handle := GetStdHandle(STD_OUTPUT_HANDLE);
for var I := 0 to 10 do
begin
var TextAttrib: integer;
var Buff: TConsoleScreenBufferInfo;
System.WriteLn('');
// Red
GetConsoleScreenBufferInfo(Handle, Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(4) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
System.WriteLn('Debug test');
//SetConsoleTextAttribute(Handle, $0F);
// Green
GetConsoleScreenBufferInfo(Handle, Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(2) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
System.WriteLn('Color filler test');
//SetConsoleTextAttribute(Handle, $0F);
// Blue
GetConsoleScreenBufferInfo(Handle, Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(1) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
System.WriteLn('Line overflow');
//SetConsoleTextAttribute(Handle, $0F);
// Multicolored line
// Red text part
GetConsoleScreenBufferInfo(Handle, Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(4) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
Write('Red text');
// Space without background
SetConsoleTextAttribute(Handle, $0F);
Write(' ');
// Green text part
GetConsoleScreenBufferInfo(Handle, Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(2) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
Write('Green text');
// Space without background
SetConsoleTextAttribute(Handle, $0F);
Write(' ');
// Blue text part
GetConsoleScreenBufferInfo(Handle, Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(1) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
Write('Blue text');
// Space without background
SetConsoleTextAttribute(Handle, $0F);
Write(' ');
// Red background colour for entire line
// We need to write string whose size matches the console line size
// which we can retrieve from TConsoleScreenBufferInfo by reading
// dwSize.X field
GetConsoleScreenBufferInfo(Handle, Buff);
TextAttrib := Buff.wAttributes and $FF0F;
TextAttrib := TextAttrib or (Word(4) shl 4);
SetConsoleTextAttribute(Handle, TextAttrib);
System.WriteLn('');
var S: String;
S := 'Red background for entire line';
SetLength(S, Buff.dwSize.X);
System.WriteLn(S);
System.WriteLn('');
end;
ReadLn;
我还添加了两个更改背景颜色的示例。
第一个显示如何在一行中获得多种不同的背景颜色。它使用
Write
而不是 WiteLn
来编写单行的各个部分。这是可能的,因为 SetConsoleTextAttribute
会影响之后写入的所有文本。
第二个显示如何获得整行的彩色背景。为此,您需要编写大小与控制台行大小匹配的字符串。
请注意,在 Windows 上调整控制台窗口大小会导致行中最后一个文本字符的文本属性应用到行尾。
我不确定这段代码在 Linux 上的运行是否与 Windows 上的运行相同,因为我还没有测试过它。