cygwin application on MsTerminal, enabling win32-raw-mode results in runway memory/CPU usage.
Adamyg Mob
adamyg.mob@gmail.com
Fri Aug 30 11:56:34 GMT 2024
Cygwin: CYGWIN_NT-10.0-19045 WEED3 3.5.4-1.x86_64 2024-08-25 16:52 UTC
x86_64 Cygwin
Windows Terminal version: 1.20.11781.0
Windows build number: 10.0.19045.4780
When running a cygwin64 based terminal application under a MsTerminal
session, with win32-raw-mode "\033[?9001h" enabled,
after brief input in the application becoming unresponsive and the session
rapidly consumes all available memory/cpu..
Test application attached, plus ticket
https://github.com/microsoft/terminal/issues/17824 contains additional
information.
Regards
-------------- next part --------------
/*
* win32-input-mode key test
*
* Build: gcc -o rawkey rawkey.c
* Usage: rawkey [--noraw] [--mouse]
*/
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>
typedef struct _MSTERMINAL_INPUT_RECORD {
unsigned bKeyDown;
unsigned wRepeatCount;
unsigned wVirtualKeyCode;
unsigned wVirtualScanCode;
union {
unsigned UnicodeChar;
char AsciiChar;
} uChar;
unsigned dwControlKeyState;
#define ENHANCED_KEY 0x0100
#define LEFT_ALT_PRESSED 0x0002
#define LEFT_CTRL_PRESSED 0x0008
#define RIGHT_ALT_PRESSED 0x0001
#define RIGHT_CTRL_PRESSED 0x0004
#define SHIFT_PRESSED 0x0010
} MSTERMINAL_INPUT_RECORD;
static void usage(void);
static void test(int win32_input_mode, int mouse_mode);
static void hex(const void *buf, unsigned len);
static const char *mskey(MSTERMINAL_INPUT_RECORD *ke, const char *buf, const char *end);
static const char *progname = "";
static const char *short_options = "rmh";
static struct option long_options[] = {
{"noraw", no_argument, NULL, 'r'},
{"mouse", no_argument, NULL, 'm'},
{"help", no_argument, 0, 'h'},
{0}
};
int
main(int argc, char **argv)
{
int win32_input_mode = 1, mouse_mode = 0;
int optidx = 0, c;
progname = basename(argv[0]);
while ((c = getopt_long(argc, argv, short_options, long_options, &optidx)) != EOF) {
switch (c) {
case 'r': // -r,--noraw
win32_input_mode = 0;
break;
case 'm': // -m,--mouse
mouse_mode = 1;
break;
case 'h': // -h,--help
usage();
default:
return EXIT_FAILURE;
}
}
argv += optind;
if ((argc -= optind) != 0) {
printf("%s: unexpected argument(s) %s\n", progname, argv[0]);
usage();
}
test(win32_input_mode, mouse_mode);
return 0;
}
static void
usage(void)
{
printf("\nUsage %s: rawkey [--noraw] [--mouse]\n", progname);
exit(EXIT_FAILURE);
}
static void
prints(const char *str)
{
fputs(str, stdout);
}
static void
test(int win32_input_mode, int mouse_mode)
{
struct termios bak, cfg;
MSTERMINAL_INPUT_RECORD key;
char buf[128];
int cnt, q;
tcgetattr(STDIN_FILENO, &cfg);
bak = cfg;
cfmakeraw(&cfg);
cfg.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSADRAIN, &cfg);
if (win32_input_mode) { // enable win32-input-mode.
prints("\033[?9001h");
}
if (mouse_mode) {
prints("\x1b[?1002h"); // enable cell-motion tracking.
prints("\x1b[?1006h"); // enable SGR extended mouse mode.
}
printf("\n\rraw-mode (press 'q' twice to exit, raw=%d, mouse=%d):\n\r", win32_input_mode, mouse_mode);
fflush(stdout);
for (q = 0; (cnt = read(STDIN_FILENO, buf, sizeof(buf))) > 0;) {
hex(buf, cnt);
if (mskey(&key, buf, buf + cnt)) {
printf(" Key: %s-%s%s%s%s%s%sVK=0x%02x/%u, UC=0x%04x/%u/%c, SC=0x%x/%u\n\r",
(key.bKeyDown ? "DN" : "UP"),
((key.dwControlKeyState & ENHANCED_KEY) ? "Enh-" : ""),
(key.dwControlKeyState & LEFT_ALT_PRESSED) ? "LAlt-" : "",
(key.dwControlKeyState & RIGHT_ALT_PRESSED) ? "RAlt-" : "",
(key.dwControlKeyState & LEFT_CTRL_PRESSED) ? "LCtrl-" : "",
(key.dwControlKeyState & RIGHT_CTRL_PRESSED) ? "RCtrl-" : "",
(key.dwControlKeyState & SHIFT_PRESSED) ? "Shift-" : "",
key.wVirtualKeyCode, key.wVirtualKeyCode,
key.uChar.UnicodeChar, key.uChar.UnicodeChar,
(key.uChar.UnicodeChar && key.uChar.UnicodeChar < 255 ? key.uChar.UnicodeChar : ' '),
key.wVirtualScanCode, key.wVirtualScanCode);
if (key.bKeyDown) {
if (key.uChar.UnicodeChar == 'q') {
if (++q == 2) {
break;
}
} else {
q = 0;
}
}
} else { // ascii
if (buf[0] == 'q') {
if (++q == 2) {
break;
}
} else {
q = 0;
}
}
fflush(stdout);
}
if (mouse_mode) {
prints("\x1b[?1006l"); // disable SGR extended mouse mode.
prints("\x1b[?1002l"); // disable cell-motion tracking.
}
if (win32_input_mode)
prints("\033[?9001l"); // disable win32-input-mode.
fflush(stdout);
tcsetattr(STDIN_FILENO, TCSADRAIN, &bak);
}
static void
hex(const void *buf, unsigned len)
{
#define HEXWIDTH 32
#define HEXCOLUMN 4
const unsigned char *cursor = buf, *end = cursor + len;
unsigned offset = 0;
for (offset = 0; cursor < end; offset += HEXWIDTH) {
const unsigned char *data = cursor;
printf("%04x: ", offset);
for (unsigned col = 0; col < HEXWIDTH; ++col) {
if ((col % HEXCOLUMN) == 0 && col)
prints(" |");
if (data == end) {
prints(" ");
} else {
printf(" %02x", *data++);
}
}
prints(" ");
for (unsigned col = 0; col < HEXWIDTH && cursor < end; ++col) {
const unsigned char c = *cursor++;
printf("%c", (c >= ' ' && c < 0x7f ? c : '.'));
}
prints("\n\r");
}
fflush(stdout);
}
//
// Decode a MSTerminal specific key escape sequence.
//
enum {
msVirtualKeyCode,
msVirtualScanCode,
msUnicodeChar,
msKeyDown,
msControlKeyState,
msRepeatCount,
msgArgumentMax
};
static const char *
DecodeMSTerminalArgs(unsigned arguments[msgArgumentMax], const char *buffer, const char *end)
{
const char *cursor = buffer + 2;
unsigned args = 0, digits = 0;
while (cursor < end && *cursor) {
const unsigned char c = *cursor++;
if (c >= '0' && c <= '9') {
if (0 == digits++) {
if (args >= msgArgumentMax) {
break; // overflow
}
arguments[args] = (c - '0');
} else {
arguments[args] = (arguments[args] * 10) + (c - '0');
}
} else if (c == ';' || c == '_') {
digits = 0;
++args;
if (c == '_') { // terminator
return (args >= 1 ? cursor : NULL);
}
} else {
args = 0; // non-digit
break;
}
}
return NULL;
}
static const char *
mskey(MSTERMINAL_INPUT_RECORD *ke, const char *buf, const char *end)
{
if ((buf + 6) < end && buf[0] == '\033' && buf[1] == '[' && buf[2]) {
/*
* Terminal win32-input-mode
*
* <ESC>[17;29;0;1;8;1_
*
* Format:
*
* <ESC>[Vk;Sc;Uc;Kd;Cs;Rc_
*
* Vk: the value of wVirtualKeyCode - any number. If omitted, defaults to '0'.
* Sc: the value of wVirtualScanCode - any number. If omitted, defaults to '0'.
* Uc: the decimal value of UnicodeChar - for example, NUL is "0", LF is
* "10", the character 'A' is "65". If omitted, defaults to '0'.
* Kd: the value of bKeyDown - either a '0' or '1'. If omitted, defaults to '0'.
* Cs: the value of dwControlKeyState - any number. If omitted, defaults to '0'.
* Rc: the value of wRepeatCount - any number. If omitted, defaults to '1'.
*
* Reference
* https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
*/
unsigned args[msgArgumentMax] = { 0, 0, 0, 0, 0, 1 };
const char *cursor;
if (NULL != (cursor = DecodeMSTerminalArgs(args, buf, end))) {
memset(ke, 0, sizeof(*ke));
ke->bKeyDown = args[msKeyDown] ? 1U : -0U;
ke->wRepeatCount = args[msRepeatCount];
ke->wVirtualKeyCode = args[msVirtualKeyCode];
ke->wVirtualScanCode = args[msVirtualScanCode];
ke->uChar.UnicodeChar = args[msUnicodeChar];
ke->dwControlKeyState = args[msControlKeyState];
return cursor;
}
}
return NULL;
}
//end
More information about the Cygwin
mailing list