microsoft / node-pty

Fork pseudoterminals in Node.JS
Other
1.46k stars 235 forks source link

Backspace Not Working #509

Closed pegvin closed 2 years ago

pegvin commented 2 years ago

Environment details

Issue description

When pressing Backspace key, it wierdly adds a blank space instead of removing the character.

My Code:

const os = require('os')
const pty = require('node-pty')
const process = require('process')
const { msleep } = require('sleep');
const { readFileSync, writeFileSync } = require('fs');
const { exit } = require('process');

const usage = `Usage: term-record [OPTION]

OPTION:
  play [Filename]        Play a recorded .json file
  record [Filename]      Record your terminal session to a .json file`

var shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash'
var lastRecordTimestamp = null
var recording = []
var args = process.argv
args.splice(0, 2)

function getDuration() {
    var now = new Date().getMilliseconds()
    var duration = now - lastRecordTimestamp
    lastRecordTimestamp = new Date().getMilliseconds()
    return duration
}

function play(filename) {
    try {
        var data = readFileSync(filename, { encoding: 'utf-8', flag: 'r'})
    } catch (err) {
        if (err.code == 'ENOENT') {
            console.error("Error: File Not Found!")
            exit(1)
        } else {
            console.error(err)
            exit(1)
        }
    }

    try {
        data = JSON.parse(data)     
    } catch (err) {
        console.error("Error: Invalid File!");
        exit(1)
    }

    console.log("------------ STARTING ------------");
    for (let i = 0; i < data.length; i++) {
        process.stdout.write(data[i].content);
        msleep(data[i].delay)
    }
    console.log("-------------- END ---------------");
}

function onPtyData(data) {
    process.stdout.write(data)
    var duration = getDuration();

    if (duration < 5) {
        duration = 100
    }

    recording.push({
        delay: Math.abs(duration),
        content: data
    });
}

function onPtyExit(filename) {
    recording[0].delay = 1000

    try {
        writeFileSync(filename, JSON.stringify(recording, null, '\t')); // JSON.stringify(recording, null, '\t') For Tabs
    } catch (err) {
        console.log(err);
    }

    process.stdin.setRawMode(false);
    process.stdin.pause()
}

function record(filename) {
    var ptyProcess = pty.spawn(shell, [], {
        name: 'TermRecord Session',
        cols: process.stdout.columns,
        rows: process.stdout.rows,
        cwd: process.cwd(),
        env: process.env
    });

    var onInput = ptyProcess.write.bind(ptyProcess)

    ptyProcess.on('data', onPtyData);
    ptyProcess.on('exit', () => {
        process.stdin.removeListener('data', onInput);
        onPtyExit(filename)
    })
    process.stdin.on('data', onInput)

    process.stdout.setDefaultEncoding('utf-8');
    process.stdin.setEncoding('utf-8')
    process.stdin.setRawMode(true)
    process.stdin.resume();
}

if (args.length === 2) {
    var file = args[1]
    if (args[0] == "record") {
        console.info("Setting App Mode to 'Record'")
        console.info("Setting Output file To '" + file + "'")
        record(file)
    }
    if (args[0] == "play") {
        console.info("Setting App Mode to 'Play'")
        console.info("Setting Input file To '" + file + "'")
        play(file)
    }
} else {
    console.log(usage);
}
Tyriar commented 2 years ago

I haven't tried hooking up stdin to node-pty, do you know what character is getting sent to it when you hit backspace? It should be 0x08 (\b).

pegvin commented 2 years ago

I haven't tried hooking up stdin to node-pty, do you know what character is getting sent to it when you hit backspace? It should be 0x08 (\b).

It sends a blank space, " " just This

Tyriar commented 2 years ago

Looks like this is a consequence of hooking it up to stdin then, it's more of a problem on the node side of things as you're giving node-pty the wrong thing.

pegvin commented 2 years ago

Looks like this is a consequence of hooking it up to stdin then, it's more of a problem on the node side of things as you're giving node-pty the wrong thing.

So any idea how can I fix it? Like I am not a professional with psuedo-terminals, it was this project named Terminalizer, from which I am seeing how things are working and writing my code. Wierd thing is this project uses @faressoft/node-pty-prebuilt as one of it's dependency, but I don't know what's the difference between this dependency and node-pty

jerch commented 2 years ago

There are several points of failure possible here. And you may not like it, but properly chaining PTYs requires some knowledge about PTY semantics and termios settings. First try to understand the layers involved in your setup:

input: terminal --> master: PTY(raw) :slave --> stdin: nodejs process --> ptyProcess.write(2nd PTY master) :slave --> target process
output: target process :stdout --> ptyProcess.onData(2nd PTY master)  --> nodejs process :stdout --> slave: PTY(raw) :master --> terminal

possible points of failure:

Hope this helps.

pegvin commented 2 years ago

There are several points of failure possible here. And you may not like it, but properly chaining PTYs requires some knowledge about PTY semantics and termios settings. First try to understand the layers involved in your setup:

input: terminal --> master: PTY(raw) :slave --> stdin: nodejs process --> ptyProcess.write(2nd PTY master) :slave --> target process
output: target process :stdout --> ptyProcess.onData(2nd PTY master)  --> nodejs process :stdout --> slave: PTY(raw) :master --> terminal

possible points of failure:

* Terminal might try to guess the BS character based on initial termios settings of PTY (most will send either DEL or BS). Depends on the terminal.

* PTY(raw) might not be fully in raw mode, if nodejs has a different idea about raw mode. You should check termios, which settings are applied. For full remote control of the 2nd PTY from inputs at the terminal the first PTY should be fully in raw mode to pass through everything unmodified.

* Check termios of 2nd PTY, esp. for control chars. They must match the expectations of the terminal (here DEL char).

* For proper screen replay later on you gonna need to store more details - ideally the replay terminal has exactly the same initial settings (the data might contain terminal state altering sequences), the same viewport size (otherwise you will see render artefacts) and the PTY termios goes through the same setting changes as done by the target process.

* For control recording to remote control an app later on (expect like functionality) you need to store the inputs, which must be band synchronous to the output data (thus needs placement markers, or at least rough timing constraints).

Hope this helps.

For sure i'll have hard time understanding that, but thanks, i am new to pty and all that stuff so it may take me a while to see what i did wrong after reading your answer

Thanks for giving your time for me.