1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-21 23:04:45 -05:00

feat(unstable): add cbreak option to setRaw (#8383)

This commit is contained in:
Marcus Hultman 2020-11-30 10:08:03 -06:00 committed by GitHub
parent 7a4d0fc22b
commit c7276e15e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 15 deletions

View file

@ -815,6 +815,10 @@ declare namespace Deno {
windowChange: () => SignalStream;
};
export type SetRawOptions = {
cbreak: boolean;
};
/** **UNSTABLE**: new API, yet to be vetted
*
* Set TTY to be under raw mode or not. In raw mode, characters are read and
@ -823,11 +827,19 @@ declare namespace Deno {
* Reading from a TTY device in raw mode is faster than reading from a TTY
* device in canonical mode.
*
* The `cbreak` option can be used to indicate that characters that correspond
* to a signal should still be generated. When disabling raw mode, this option
* is ignored. This functionality currently only works on Linux and Mac OS.
*
* ```ts
* Deno.setRaw(myTTY.rid, true);
* Deno.setRaw(myTTY.rid, true, { cbreak: true });
* ```
*/
export function setRaw(rid: number, mode: boolean): void;
export function setRaw(
rid: number,
mode: boolean,
options?: SetRawOptions,
): void;
/** **UNSTABLE**: needs investigation into high precision time.
*

View file

@ -5,6 +5,7 @@ use super::io::StreamResource;
use super::io::StreamResourceHolder;
use deno_core::error::bad_resource_id;
use deno_core::error::last_os_error;
use deno_core::error::not_supported;
use deno_core::error::resource_unavailable;
use deno_core::error::AnyError;
use deno_core::serde_json;
@ -15,8 +16,6 @@ use deno_core::ZeroCopyBuf;
use serde::Deserialize;
use serde::Serialize;
#[cfg(unix)]
use deno_core::error::not_supported;
#[cfg(unix)]
use nix::sys::termios;
@ -53,10 +52,17 @@ pub fn init(rt: &mut deno_core::JsRuntime) {
super::reg_json_sync(rt, "op_console_size", op_console_size);
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct SetRawOptions {
cbreak: bool,
}
#[derive(Deserialize)]
struct SetRawArgs {
rid: u32,
mode: bool,
options: SetRawOptions,
}
fn op_set_raw(
@ -69,6 +75,7 @@ fn op_set_raw(
let args: SetRawArgs = serde_json::from_value(args)?;
let rid = args.rid;
let is_raw = args.mode;
let cbreak = args.options.cbreak;
// From https://github.com/kkawakam/rustyline/blob/master/src/tty/windows.rs
// and https://github.com/kkawakam/rustyline/blob/master/src/tty/unix.rs
@ -86,6 +93,9 @@ fn op_set_raw(
if resource_holder.is_none() {
return Err(bad_resource_id());
}
if cbreak {
return Err(not_supported());
}
let resource_holder = resource_holder.unwrap();
// For now, only stdin.
@ -164,15 +174,13 @@ fn op_set_raw(
}
};
if maybe_tty_mode.is_some() {
// Already raw. Skip.
return Ok(json!({}));
if maybe_tty_mode.is_none() {
// Save original mode.
let original_mode = termios::tcgetattr(raw_fd)?;
maybe_tty_mode.replace(original_mode);
}
let original_mode = termios::tcgetattr(raw_fd)?;
let mut raw = original_mode.clone();
// Save original mode.
maybe_tty_mode.replace(original_mode);
let mut raw = maybe_tty_mode.clone().unwrap();
raw.input_flags &= !(termios::InputFlags::BRKINT
| termios::InputFlags::ICRNL
@ -184,8 +192,10 @@ fn op_set_raw(
raw.local_flags &= !(termios::LocalFlags::ECHO
| termios::LocalFlags::ICANON
| termios::LocalFlags::IEXTEN
| termios::LocalFlags::ISIG);
| termios::LocalFlags::IEXTEN);
if !cbreak {
raw.local_flags &= !(termios::LocalFlags::ISIG);
}
raw.control_chars[termios::SpecialCharacterIndices::VMIN as usize] = 1;
raw.control_chars[termios::SpecialCharacterIndices::VTIME as usize] = 0;
termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &raw)?;

View file

@ -11,8 +11,13 @@
return core.jsonOpSync("op_isatty", { rid });
}
function setRaw(rid, mode) {
core.jsonOpSync("op_set_raw", { rid, mode });
const DEFAULT_SET_RAW_OPTIONS = {
cbreak: false,
};
function setRaw(rid, mode, options = {}) {
const rOptions = { ...DEFAULT_SET_RAW_OPTIONS, ...options };
core.jsonOpSync("op_set_raw", { rid, mode, options: rOptions });
}
window.__bootstrap.tty = {

View file

@ -218,6 +218,39 @@ pub fn test_raw_tty() {
}
}
#[cfg(unix)]
#[test]
pub fn test_raw_tty_cbreak() {
use std::io::{Read, Write};
use util::pty::fork::*;
let deno_exe = util::deno_exe_path();
let root_path = util::root_path();
let fork = Fork::from_ptmx().unwrap();
if let Ok(mut master) = fork.is_parent() {
let mut obytes: [u8; 100] = [0; 100];
let mut nread = master.read(&mut obytes).unwrap();
assert_eq!(String::from_utf8_lossy(&obytes[0..nread]), "S");
master.write_all(&[3]).unwrap(); // send SIGINT
master.flush().unwrap();
nread = master.read(&mut obytes).unwrap();
assert_eq!(String::from_utf8_lossy(&obytes[0..nread]), "A");
fork.wait().unwrap();
} else {
// Keep echo enabled such that 'C^' would be printed in non-raw mode.
std::env::set_current_dir(root_path).unwrap();
let err = exec::Command::new(deno_exe)
.arg("run")
.arg("--unstable")
.arg("--quiet")
.arg("--no-check")
.arg("cli/tests/raw_mode_cbreak.ts")
.exec();
println!("err {}", err);
unreachable!()
}
}
#[test]
fn test_pattern_match() {
// foo, bar, baz, qux, quux, quuz, corge, grault, garply, waldo, fred, plugh, xyzzy

View file

@ -0,0 +1,15 @@
Deno.setRaw(0, true);
Deno.setRaw(0, true, { cbreak: true }); // Can be called multiple times
const signal = Deno.signals.interrupt();
Deno.stdout.writeSync(new TextEncoder().encode("S"));
await signal;
Deno.stdout.writeSync(new TextEncoder().encode("A"));
signal.dispose();
Deno.setRaw(0, false); // restores old mode.
Deno.setRaw(0, false); // Can be safely called multiple times