Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions doc/api/tty.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,18 @@ A `boolean` that is always `true` for `tty.ReadStream` instances.

<!-- YAML
added: v0.7.7
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/REPLACEME
description: The `mode` argument supports `'raw-vt'` and `'io'`.
-->

* `mode` {boolean} If `true`, configures the `tty.ReadStream` to operate as a
raw device. If `false`, configures the `tty.ReadStream` to operate in its
default mode. The `readStream.isRaw` property will be set to the resulting
mode.
* `mode` {boolean|string} If `true` or `'raw-vt'`, configures the
`tty.ReadStream` to operate as a raw device. If `'io'`, configures the
`tty.ReadStream` to operate in binary-safe I/O mode. If `false`, configures
the `tty.ReadStream` to operate in its default mode. The `readStream.isRaw`
property will be set to whether the stream is in raw mode, and the
`readStream.rawMode` property will be set to the resulting mode.
* Returns: {this} The read stream instance.

Allows configuration of `tty.ReadStream` so that it operates as a raw device.
Expand All @@ -86,6 +92,22 @@ characters. <kbd>Ctrl</kbd>+<kbd>C</kbd> will no longer cause a `SIGINT` when
in this mode. This mode does not affect terminal output processing, such as
newline translation on Unix terminals.

When in binary-safe I/O mode, terminal output processing is also disabled.
This corresponds to libuv's `UV_TTY_MODE_IO` mode and is not supported on
Windows.

### `readStream.rawMode`

<!-- YAML
added: REPLACEME
-->

* {boolean|string}

The current raw mode for the `tty.ReadStream`. This is `false` when the stream
is in its default mode, `'raw-vt'` when raw input mode is enabled, and `'io'`
when binary-safe I/O mode is enabled.

## Class: `tty.WriteStream`

<!-- YAML
Expand Down
24 changes: 19 additions & 5 deletions lib/tty.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ const {
} = primordials;

const net = require('net');
const { TTY, isTTY } = internalBinding('tty_wrap');
const {
TTY,
UV_TTY_MODE_IO,
UV_TTY_MODE_NORMAL,
UV_TTY_MODE_RAW_VT,
isTTY,
} = internalBinding('tty_wrap');
const {
ErrnoException,
codes: {
Expand Down Expand Up @@ -68,20 +74,28 @@ function ReadStream(fd, options) {
});

this.isRaw = false;
this.rawMode = false;
this.isTTY = true;
}

ObjectSetPrototypeOf(ReadStream.prototype, net.Socket.prototype);
ObjectSetPrototypeOf(ReadStream, net.Socket);

ReadStream.prototype.setRawMode = function(flag) {
flag = !!flag;
const err = this._handle?.setRawMode(flag);
ReadStream.prototype.setRawMode = function(mode) {
const rawMode = mode === 'io' ? 'io' : (mode ? 'raw-vt' : false);
let ttyMode = UV_TTY_MODE_NORMAL;
if (rawMode === 'io') {
ttyMode = UV_TTY_MODE_IO;
} else if (rawMode === 'raw-vt') {
ttyMode = UV_TTY_MODE_RAW_VT;
}
const err = this._handle?.setRawMode(ttyMode);
if (err) {
this.emit('error', new ErrnoException(err, 'setRawMode'));
return this;
}
this.isRaw = flag;
this.isRaw = rawMode !== false;
this.rawMode = rawMode;
return this;
};

Expand Down
10 changes: 7 additions & 3 deletions src/tty_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ void TTYWrap::Initialize(Local<Object> target,
SetProtoMethod(isolate, t, "setRawMode", SetRawMode);

SetMethodNoSideEffect(context, target, "isTTY", IsTTY);
NODE_DEFINE_CONSTANT(target, UV_TTY_MODE_NORMAL);
NODE_DEFINE_CONSTANT(target, UV_TTY_MODE_IO);
NODE_DEFINE_CONSTANT(target, UV_TTY_MODE_RAW_VT);

Local<Value> func;
if (t->GetFunction(context).ToLocal(&func) &&
Expand Down Expand Up @@ -124,9 +127,10 @@ void TTYWrap::SetRawMode(const FunctionCallbackInfo<Value>& args) {
// sequences at all on Windows, such as bracketed paste mode.
// The Node.js readline implementation handles differences between
// these modes.
int err = uv_tty_set_mode(
&wrap->handle_,
args[0]->IsTrue() ? UV_TTY_MODE_RAW_VT : UV_TTY_MODE_NORMAL);
Environment* env = Environment::GetCurrent(args);
int mode;
if (!args[0]->Int32Value(env->context()).To(&mode)) return;
int err = uv_tty_set_mode(&wrap->handle_, static_cast<uv_tty_mode_t>(mode));
args.GetReturnValue().Set(err);
}

Expand Down
36 changes: 36 additions & 0 deletions test/pseudo-tty/test-set-raw-mode-modes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict';
require('../common');
const assert = require('assert');
const { spawnSync } = require('child_process');

function isOnlcrEnabled() {
const { stdout, status } = spawnSync('stty', ['-a'], {
encoding: 'utf8',
stdio: ['inherit', 'pipe', 'inherit'],
});

assert.strictEqual(status, 0);
return /(?:^|[\s;])onlcr(?:[\s;]|$)/.test(stdout);
}

process.stdin.setRawMode(true);
console.log(`raw=${isOnlcrEnabled()}`);
assert.strictEqual(process.stdin.isRaw, true);
assert.strictEqual(process.stdin.rawMode, 'raw-vt');

process.stdin.setRawMode(false);
console.log(`normal=${process.stdin.isRaw}`);
assert.strictEqual(process.stdin.rawMode, false);

process.stdin.setRawMode('raw-vt');
console.log(`raw-vt=${isOnlcrEnabled()}`);
assert.strictEqual(process.stdin.isRaw, true);
assert.strictEqual(process.stdin.rawMode, 'raw-vt');

process.stdin.setRawMode(false);
process.stdin.setRawMode('io');
console.log(`io=${isOnlcrEnabled()}`);
assert.strictEqual(process.stdin.isRaw, true);
assert.strictEqual(process.stdin.rawMode, 'io');

process.stdin.setRawMode(false);
4 changes: 4 additions & 0 deletions test/pseudo-tty/test-set-raw-mode-modes.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
raw=true
normal=false
raw-vt=true
io=false