summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/dts/lib.deno.unstable.d.ts16
-rw-r--r--cli/ops/tty.rs32
-rw-r--r--cli/rt/40_tty.js9
-rw-r--r--cli/tests/integration_tests.rs33
-rw-r--r--cli/tests/raw_mode_cbreak.ts15
5 files changed, 90 insertions, 15 deletions
diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts
index 667b1fbba..d8e226699 100644
--- a/cli/dts/lib.deno.unstable.d.ts
+++ b/cli/dts/lib.deno.unstable.d.ts
@@ -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.
*
diff --git a/cli/ops/tty.rs b/cli/ops/tty.rs
index 52c89ee11..be1d7d3e4 100644
--- a/cli/ops/tty.rs
+++ b/cli/ops/tty.rs
@@ -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;
@@ -16,8 +17,6 @@ use serde::Deserialize;
use serde::Serialize;
#[cfg(unix)]
-use deno_core::error::not_supported;
-#[cfg(unix)]
use nix::sys::termios;
#[cfg(windows)]
@@ -54,9 +53,16 @@ pub fn init(rt: &mut deno_core::JsRuntime) {
}
#[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)?;
diff --git a/cli/rt/40_tty.js b/cli/rt/40_tty.js
index b50b7668c..598d33237 100644
--- a/cli/rt/40_tty.js
+++ b/cli/rt/40_tty.js
@@ -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 = {
diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs
index 8ad4ea5ed..1f40acbcb 100644
--- a/cli/tests/integration_tests.rs
+++ b/cli/tests/integration_tests.rs
@@ -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
diff --git a/cli/tests/raw_mode_cbreak.ts b/cli/tests/raw_mode_cbreak.ts
new file mode 100644
index 000000000..6506e89d7
--- /dev/null
+++ b/cli/tests/raw_mode_cbreak.ts
@@ -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