mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
feat(unstable): add cbreak option to setRaw (#8383)
This commit is contained in:
parent
7a4d0fc22b
commit
c7276e15e5
5 changed files with 90 additions and 15 deletions
16
cli/dts/lib.deno.unstable.d.ts
vendored
16
cli/dts/lib.deno.unstable.d.ts
vendored
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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)?;
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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
|
||||
|
|
15
cli/tests/raw_mode_cbreak.ts
Normal file
15
cli/tests/raw_mode_cbreak.ts
Normal 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
|
Loading…
Reference in a new issue