Skip to content

notcurses_init() hangs forever under WSL in Windows Terminal #2929

Description

@robinpie

Please include the following data:

  • export | egrep 'LANG|LC_CTYPE|TERM':
declare -x LANG="en_US.UTF-8"
declare -x TERM="xterm-256color"
  • notcurses version (available from notcurses-demo i)
    built from latest git master as of this issue report (but i can't actually open notcurses-demo i because of this problem)
  • terminal name + version
    Windows Terminal 1.24.11321.0

under Windows Terminal, talking to WSL2 Linux via ConPTY, notcurses_init() never returns. the program enters alternate screen and then gets stuck on a black screen

attaching a debugger revealed the main thread parked in inputlayer_get_responses() (src/lib/in.c) on pthread_cond_wait, waiting for a Primary Device Attributes (DA1) reply that never comes

i think it never arrives because the kitty graphics query APC that notcurses sends in DIRECTIVES immediately before the DA1 puts conpty's escape sequence parser into a state in which it just eats the DA1

notcurses already guards against this on native Windows builds:

// we do not send this query on Windows because it is bled through ConHost,
// and echoed onto the standard output.
#ifndef __MINGW32__
#define KITTYQUERY "\x1b_Gi=1,a=q;\x1b\\"
#else
#define KITTYQUERY
#endif

but of course, this does not trigger when it's running on real linux but being accessed through windows terminal.

repro example of the issue that causes this:

// this code dedicated to the public domain. no warranty

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <termios.h>
#include <ctype.h>

static struct termios g_orig;
static void raw_on(void){
    struct termios t;
    tcgetattr(STDIN_FILENO, &g_orig);
    t = g_orig;
    cfmakeraw(&t);
    tcsetattr(STDIN_FILENO, TCSANOW, &t);
}
static void raw_off(void){ tcsetattr(STDIN_FILENO, TCSANOW, &g_orig); }

// write `query`, then collect everything that arrives within ~1.5s
static void run_case(const char *name, const char *query, size_t qlen){
    // drain anything pending first
    for(;;){
        struct pollfd p = { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 };
        char junk[256];
        if(poll(&p, 1, 50) <= 0) break;
        if(read(STDIN_FILENO, junk, sizeof junk) <= 0) break;
    }
    if(write(STDOUT_FILENO, query, qlen) < 0) return;

    char buf[4096];
    size_t n = 0;
    for(;;){
        struct pollfd p = { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 };
        if(poll(&p, 1, 1500) <= 0) break;          // timeout: terminal is done
        ssize_t r = read(STDIN_FILENO, buf + n, sizeof(buf) - n - 1);
        if(r <= 0) break;
        n += (size_t)r;
        if(n >= sizeof(buf) - 1) break;
    }

    int da1 = memmem(buf, n, "\x1b[?", 3) != NULL && n > 0 && buf[n-1] == 'c';
    raw_off();
    printf("=== %s ===\r\n  sent:", name);
    for(size_t i = 0; i < qlen; i++)
        printf(isprint((unsigned char)query[i]) ? " %c" : " \\x%02x", (unsigned char)query[i]);
    printf("\r\n  got %zu bytes: ", n);
    for(size_t i = 0; i < n; i++)
        printf(isprint((unsigned char)buf[i]) ? "%c" : ".", (unsigned char)buf[i]);
    printf("\r\n  DA1 reply seen: %s\r\n\r\n", da1 ? "YES" : "no");
    fflush(stdout);
    raw_on();
}

int main(void){
    raw_on();
    run_case("1. DA1 alone",
             "\x1b[c", 3);
    run_case("2. DA2 alone (control)",
             "\x1b[>c", 4);
    run_case("3. kitty graphics APC, then DA1  (notcurses' DIRECTIVES order)",
             "\x1b_Gi=1,a=q;\x1b\\" "\x1b[c", 11 + 3);
    raw_off();
    printf("if case 1 shows a DA1 reply but case 3 does not, the kitty graphics APC is consuming the DA1 that notcurses waits on as its init sentinel, which is why notcurses_init() never returns here.\r\n");
    return 0;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions