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