terminal
terminal copied to clipboard
Is there a flag or command to make ConPTY not echo/output the event of screen buffer size changed?
I am coding two programs, using ConPTY as a server-side run on windows, and a client runs on Linux. when the client console window size changed, the client will send '\e[8;w;ht'(w is the window real width, h is the window real height) to the server. The server received the message and writes it into the pty, but the message '\e[8;w;ht....... ' will be output back from pty and sent to the client. the client received the message and print it, and resize the window for the VT100 command, a new 'screen buffer size changed' event occurs from the client, ...... infinite loop.
We might need more details on the scenario here. A more concrete repro case.
I don't think that conpty should be re-emitting a resize for the size it was just resized to. What OS version is ConPTY running on/? There's all sorts of code around VtEngine::SuppressResizeRepaint
that should be preventing this.
This is the test code, it's can repro the case:
#include <Windows.h>
#include <stdio.h>
#include <assert.h>
#include <string>
#include <memory>
#include <time.h>
int main(int argc, char* argv[])
{
HPCON hPC = 0;
PROCESS_INFORMATION pi = { 0 };
HANDLE outPipeOurSide = nullptr, inPipeOurSide = nullptr;
HANDLE outPipePseudoConsoleSide = nullptr, inPipePseudoConsoleSide = nullptr;
int err;
bool success;
// create pty
CreatePipe(&inPipePseudoConsoleSide, &inPipeOurSide, NULL, 0);
CreatePipe(&outPipeOurSide, &outPipePseudoConsoleSide, NULL, 0);
CONSOLE_SCREEN_BUFFER_INFOEX info = { 0 };
info.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
success = GetConsoleScreenBufferInfoEx(GetStdHandle(STD_OUTPUT_HANDLE), &info);
assert(success);
short w = info.srWindow.Right - info.srWindow.Left + 1;
short h = info.srWindow.Bottom - info.srWindow.Top + 1;
err = CreatePseudoConsole(COORD{ w, h }, inPipePseudoConsoleSide, outPipePseudoConsoleSide, 0, &hPC);
assert(err == S_OK);
STARTUPINFOEXW si;
memset(&si, 0, sizeof(si));
si.StartupInfo.cb = sizeof(STARTUPINFOEXW);
size_t size;
InitializeProcThreadAttributeList(NULL, 1, 0, &size);
si.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(new BYTE[size]);
success = InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, (PSIZE_T)&size);
assert(success);
success = UpdateProcThreadAttribute(
si.lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
hPC,
sizeof(hPC),
NULL,
NULL);
assert(success);
std::wstring commandline = L"cmd.exe";
std::unique_ptr<wchar_t> bufCommandline(new wchar_t[commandline.length() + 1]);
memcpy(bufCommandline.get(), commandline.c_str(), (commandline.length() + 1) * sizeof(wchar_t));
success = CreateProcessW(
nullptr,
bufCommandline.get(),
nullptr,
nullptr,
TRUE,
EXTENDED_STARTUPINFO_PRESENT,
nullptr,
nullptr,
&si.StartupInfo,
&pi);
assert(success);
DeleteProcThreadAttributeList(si.lpAttributeList);
DWORD settings;
HANDLE hConsole;
//change code page
system("chcp 65001 > nul"); // utf-8
//print output
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
assert(hConsole != INVALID_HANDLE_VALUE);
success = GetConsoleMode(hConsole, &settings);
assert(success);
settings |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
success = SetConsoleMode(hConsole, settings);
assert(success);
const int BUF_LEN = 4096;
std::unique_ptr<char> buf(new char[BUF_LEN]);
time_t t = time(NULL);
bool writtenResize = false;
do
{
DWORD n = 0;
success = PeekNamedPipe(outPipeOurSide, nullptr, 0, nullptr, &n, nullptr);
assert(success);
if (n)
{
if (n > BUF_LEN)
n = BUF_LEN;
DWORD l;
success = ReadFile(outPipeOurSide, buf.get(), n, &l, nullptr);
assert(success);
for (int i = 0; i < l; i++)
printf("%c", buf.get()[i]);
FILE* pf = nullptr;
{
errno_t err = fopen_s(&pf, "e:/out.txt", "ab");
assert(pf);
}
int ret = fwrite(buf.get(), l, 1, pf);
assert(ret == 1);
fclose(pf);
}
if (time(NULL) - t > 5 && !writtenResize) // after 5 seconds, write resize command
{
//type command
short width = 600, height = 400;
std::string command;
command.append(1, 0x1b).append("[8;").append(std::to_string(width)).append(";").append(std::to_string(height)).append(1, 't');
DWORD l;
success = WriteFile(inPipeOurSide, command.c_str(), command.length(), &l, nullptr);
assert(success && l == command.length());
writtenResize = true;
}
} while (true);
CloseHandle(pi.hProcess);
CloseHandle(outPipeOurSide);
CloseHandle(inPipeOurSide);
CloseHandle(outPipePseudoConsoleSide);
CloseHandle(inPipePseudoConsoleSide);
ClosePseudoConsole(hPC);
return 0;
}
The output file : out.txt
CSI t
is not intended to be sent to a client application that is not prepared for it. A window resize message is supposed to be generated by a client and sent to a terminal emulator. However, on line 126 you are telling ConPTY to send CSI t
to the client application.
You should only write things into inPipeOurSide
that you want the client application to receive.
If you want to resize the console host (which will trigger the correct kind of window resize message sent to the client), you should use ResizePseudoConsole
.
Other bugs:
On line 76 you are using system
to call chcp
. This runs cmd.exe /s /c chcp 65001
. You should just call SetConsoleOutputCP
instead. It has much less overhead. When you use system
, you force the creation of another process.
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.
I changed the test codes as you said, and ran it on my one machine( Windows 11).
#include <Windows.h>
#include <stdio.h>
#include <assert.h>
#include <string>
#include <memory>
#include <time.h>
int main(int argc, char* argv[])
{
HPCON hPC = 0;
PROCESS_INFORMATION pi = { 0 };
HANDLE outPipeOurSide = nullptr, inPipeOurSide = nullptr;
HANDLE outPipePseudoConsoleSide = nullptr, inPipePseudoConsoleSide = nullptr;
int err;
bool success;
// create pty
CreatePipe(&inPipePseudoConsoleSide, &inPipeOurSide, NULL, 0);
CreatePipe(&outPipeOurSide, &outPipePseudoConsoleSide, NULL, 0);
CONSOLE_SCREEN_BUFFER_INFOEX info = { 0 };
info.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
success = GetConsoleScreenBufferInfoEx(GetStdHandle(STD_OUTPUT_HANDLE), &info);
assert(success);
short w = info.srWindow.Right - info.srWindow.Left + 1;
short h = info.srWindow.Bottom - info.srWindow.Top + 1;
err = CreatePseudoConsole(COORD{ w, h }, inPipePseudoConsoleSide, outPipePseudoConsoleSide, 0, &hPC);
assert(err == S_OK);
STARTUPINFOEXW si;
memset(&si, 0, sizeof(si));
si.StartupInfo.cb = sizeof(STARTUPINFOEXW);
size_t size;
InitializeProcThreadAttributeList(NULL, 1, 0, &size);
si.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(new BYTE[size]);
success = InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, (PSIZE_T)&size);
assert(success);
success = UpdateProcThreadAttribute(
si.lpAttributeList,
0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
hPC,
sizeof(hPC),
NULL,
NULL);
assert(success);
std::wstring commandline = L"cmd.exe";
std::unique_ptr<wchar_t> bufCommandline(new wchar_t[commandline.length() + 1]);
memcpy(bufCommandline.get(), commandline.c_str(), (commandline.length() + 1) * sizeof(wchar_t));
success = CreateProcessW(
nullptr,
bufCommandline.get(),
nullptr,
nullptr,
TRUE,
EXTENDED_STARTUPINFO_PRESENT,
nullptr,
nullptr,
&si.StartupInfo,
&pi);
assert(success);
DeleteProcThreadAttributeList(si.lpAttributeList);
DWORD settings;
HANDLE hConsole;
//print output
success = SetConsoleOutputCP(65001); //change code page
assert(success);
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
assert(hConsole != INVALID_HANDLE_VALUE);
success = GetConsoleMode(hConsole, &settings);
assert(success);
settings |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
success = SetConsoleMode(hConsole, settings);
assert(success);
const int BUF_LEN = 4096;
std::unique_ptr<char> buf(new char[BUF_LEN]);
time_t t = time(NULL);
bool writtenResize = false;
do
{
DWORD n = 0;
success = PeekNamedPipe(outPipeOurSide, nullptr, 0, nullptr, &n, nullptr);
assert(success);
if (n)
{
if (n > BUF_LEN)
n = BUF_LEN;
DWORD l;
success = ReadFile(outPipeOurSide, buf.get(), n, &l, nullptr);
assert(success);
for (int i = 0; i < l; i++)
printf("%c", buf.get()[i]);
FILE* pf = nullptr;
{
errno_t err = fopen_s(&pf, "e:/out2.txt", "ab");
assert(pf);
}
int ret = fwrite(buf.get(), l, 1, pf);
assert(ret == 1);
fclose(pf);
}
if (time(NULL) - t > 5 && !writtenResize) // after 5 seconds, write resize command
{
//type command
short width = 600, height = 400;
/*std::string command;
command.append(1, 0x1b).append("[8;").append(std::to_string(width)).append(";").append(std::to_string(height)).append(1, 't');
DWORD l;
success = WriteFile(inPipeOurSide, command.c_str(), command.length(), &l, nullptr);
assert(success && l == command.length());*/
ResizePseudoConsole(hPC, { width, height });
writtenResize = true;
}
} while (true);
CloseHandle(pi.hProcess);
CloseHandle(outPipeOurSide);
CloseHandle(inPipeOurSide);
CloseHandle(outPipePseudoConsoleSide);
CloseHandle(inPipePseudoConsoleSide);
ClosePseudoConsole(hPC);
return 0;
}
It still echoes the size change event. out2.txt But, on my other machine(Windows 10), it does not echo.
CSI t
is not intended to be sent to a client application that is not prepared for it. A window resize message is supposed to be generated by a client and sent to a terminal emulator. However, on line 126 you are telling ConPTY to sendCSI t
to the client application.You should only write things into
inPipeOurSide
that you want the client application to receive.If you want to resize the console host (which will trigger the correct kind of window resize message sent to the client), you should use
ResizePseudoConsole
.
Line 126 (at new test, it's line 130) just simulates the client window doing resize, and the ‘conpty’ synchronize it.