UserExistsError / conpty

Windows Pseudo Console (ConPTY) for Golang
MIT License
32 stars 12 forks source link

How to disable ENABLE_LINE_INPUT in ConPTY? #2

Closed lonnywong closed 1 year ago

lonnywong commented 2 years ago

The test case:

package main

import (
    "bufio"
    "context"
    "fmt"
    "strings"

    "github.com/UserExistsError/conpty"
)

func main() {
    cpty, err := conpty.Start("python3 -c \"import sys; s = sys.stdin.readline(); print(len(s))\"")
    if err != nil {
        fmt.Printf("Failed to spawn a pty:  %v", err)
        return
    }
    defer cpty.Close()

    go func() {
        cpty.Write([]byte(strings.Repeat("A", 3000) + "\r\n"))

        scanner := bufio.NewScanner(cpty)
        for scanner.Scan() {
            // Should output 3001.
            // But output 511 in cmd, output 2510 in MSYS2.
            fmt.Printf("Output: %s\n", scanner.Text())
        }
        if err := scanner.Err(); err != nil {
            fmt.Println(err)
        }
    }()

    if _, err := cpty.Wait(context.Background()); err != nil {
        fmt.Printf("Error: %v", err)
    }
}

The python read one line and output the line length.

It should output 3001. But output 511 in cmd, output 2510 in MSYS2.

lonnywong commented 2 years ago

Is it similar to https://stackoverflow.com/a/11439517/7696611 ?

lonnywong commented 2 years ago

If disable ENABLE_LINE_INPUT in the child process, it works.

But I can't change the child process to disable ENABLE_LINE_INPUT.

Is there a way to disable ENABLE_LINE_INPUT in ConPTY?

import ( "bufio" "fmt" "os"

"golang.org/x/sys/windows"

)

func main() { var st uint32 if err := windows.GetConsoleMode(windows.Handle(os.Stdin.Fd()), &st); err != nil { fmt.Println(err) return }

// disable ENABLE_LINE_INPUT
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_LINE_INPUT)
fmt.Printf("st: %#x, raw: %#x\n", st, raw)

if err := windows.SetConsoleMode(windows.Handle(os.Stdin.Fd()), raw); err != nil {
    fmt.Println(err)
    return
}

// reset console mode
defer func() {
    if err := windows.SetConsoleMode(windows.Handle(os.Stdin.Fd()), st); err != nil {
        fmt.Println(err)
        return
    }
}()

// read line
line, err := bufio.NewReader(os.Stdin).ReadBytes('\r')
if err != nil {
    fmt.Println(err)
    return
}
fmt.Printf("\nLength: %d\r\n", len(line))

}


* `go run conptytest.go`
```go
package main

import (
    "bufio"
    "context"
    "fmt"
    "strings"
    "time"

    "github.com/UserExistsError/conpty"
)

func main() {
    cpty, err := conpty.Start("readline.exe") // The child process have to disable ENABLE_LINE_INPUT first
    if err != nil {
        fmt.Printf("Failed to spawn a pty:  %v", err)
        return
    }
    defer cpty.Close()

    go func() {
        time.Sleep(1 * time.Second) // Wait the child process disable ENABLE_LINE_INPUT first
        cpty.Write([]byte(strings.Repeat("A", 5000) + "\r\n"))

        scanner := bufio.NewScanner(cpty)
        for scanner.Scan() {
            fmt.Println(scanner.Text())
        }
        if err := scanner.Err(); err != nil {
            fmt.Println(err)
        }
    }()

    if _, err := cpty.Wait(context.Background()); err != nil {
        fmt.Printf("Error: %v", err)
    }
}
lonnywong commented 1 year ago

Maybe it's an issue about Windows stdin.