Skip to content

Commit

Permalink
tty driver ignore lnext (\cV) and werase (\cW)
Browse files Browse the repository at this point in the history
Configure the tty driver to ignore the lnext (\cV) and werase (\cW) characters
so they can be bound to fish functions.

Correct the `fish_key_bindings` program to initialize the tty in the same
manner as the `fish` program.

Fixes #3064
  • Loading branch information
Kurtis Rader committed Jun 20, 2016
1 parent 693d687 commit a243580
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 65 deletions.
52 changes: 11 additions & 41 deletions src/fish_key_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <termios.h>
#include <wctype.h>
#include <string>

Expand All @@ -23,15 +22,16 @@
#include "fallback.h" // IWYU pragma: keep
#include "input.h"
#include "input_common.h"
#include "proc.h"
#include "reader.h"

struct config_paths_t determine_config_directory_paths(const char *argv0);

static struct termios saved_modes; // so we can reset the modes when we're done
static long long int prev_tstamp = 0;
static const char *ctrl_equivalents[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\a",
"\\b", "\\t", "\\n", "\\v", "\\f", "\\r", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "\\e", NULL, NULL, NULL, NULL};
"\\b", "\\t", "\\n", "\\v", "\\f", "\\r", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, "\\e", NULL, NULL, NULL, NULL};

/// Return true if the recent sequence of characters indicates the user wants to exit the program.
bool should_exit(unsigned char c) {
Expand All @@ -45,7 +45,7 @@ bool should_exit(unsigned char c) {
}

/// Return the key name if the recent sequence of characters matches a known terminfo sequence.
char * const key_name(unsigned char c) {
char *const key_name(unsigned char c) {
static char recent_chars[8] = {0};

recent_chars[0] = recent_chars[1];
Expand Down Expand Up @@ -119,7 +119,7 @@ void process_input(bool continuous_mode) {
printf("%6lld usec dec: %3u hex: %2x char: %c\n", delta_tstamp, c, c, c);
}

char * const name = key_name(c);
char *const name = key_name(c);
if (name) {
printf("FYI: Saw sequence for bind key name \"%s\"\n", name);
free(name);
Expand All @@ -134,48 +134,22 @@ void process_input(bool continuous_mode) {
}
}

/// Set the tty modes to not interpret any characters. We want every character to be passed thru to
/// this program. Including characters such as [ctrl-C] and [ctrl-D] that might normally have
/// special significance (e.g., terminate the program).
bool set_tty_modes(void) {
struct termios modes;

tcgetattr(0, &modes); // get the current tty modes
saved_modes = modes; // save a copy so we can reset them on exit

modes.c_lflag &= ~ICANON; // turn off canonical mode
modes.c_lflag &= ~ECHO; // turn off echo mode
modes.c_lflag &= ~ISIG; // turn off recognizing signal generating characters
modes.c_iflag &= ~ICRNL; // turn off mapping CR to NL
modes.c_iflag &= ~INLCR; // turn off mapping NL to CR
modes.c_cc[VMIN] = 1; // return each character as they arrive
modes.c_cc[VTIME] = 0; // wait forever for the next character

if (tcsetattr(0, TCSANOW, &modes) != 0) { // set the new modes
return false;
}
return true;
}

/// Restore the tty modes to what they were before this program was run. This shouldn't be required
/// but we do it just in case the program that ran us doesn't handle tty modes for external programs
/// in a sensible manner.
void reset_tty_modes() { tcsetattr(0, TCSANOW, &saved_modes); }

/// Make sure we cleanup before exiting if we're signaled.
void signal_handler(int signo) {
printf("\nExiting on receipt of signal #%d\n", signo);
reset_tty_modes();
restore_term_mode();
exit(1);
}

/// Setup our environment (e.g., tty modes), process key strokes, then reset the environment.
void setup_and_process_keys(bool continuous_mode) {
is_interactive_session = 1; // by definition this is interactive
set_main_thread();
setup_fork_guards();
wsetlocale(LC_ALL, L"POSIX");
program_name = L"fish_key_reader";
env_init();
reader_init();
input_init();

// Installing our handler for every signal (e.g., SIGSEGV) is dubious because it means that
Expand All @@ -196,13 +170,9 @@ void setup_and_process_keys(bool continuous_mode) {
set_wait_on_escape_ms(500);
}

if (!set_tty_modes()) {
printf("Could not set the tty modes. Refusing to continue running.\n");
exit(1);
}
// TODO: We really should enable keypad mode but see issue #838.
process_input(continuous_mode);
reset_tty_modes();
restore_term_mode();
}

int main(int argc, char **argv) {
Expand Down
42 changes: 18 additions & 24 deletions src/reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1026,31 +1026,25 @@ void reader_init()

/* Set the mode used for the terminal, initialized to the current mode */
memcpy(&shell_modes, &terminal_mode_on_startup, sizeof shell_modes);
shell_modes.c_iflag &= ~ICRNL; /* turn off mapping CR (\cM) to NL (\cJ) */
shell_modes.c_iflag &= ~INLCR; /* turn off mapping NL (\cJ) to CR (\cM) */
shell_modes.c_lflag &= ~ICANON; /* turn off canonical mode */
shell_modes.c_lflag &= ~ECHO; /* turn off echo mode */
shell_modes.c_iflag &= ~IXON; /* disable flow control */
shell_modes.c_iflag &= ~IXOFF; /* disable flow control */
shell_modes.c_cc[VMIN]=1;
shell_modes.c_cc[VTIME]=0;

#if defined(_POSIX_VDISABLE)
// PCA disable VDSUSP (typically control-Y), which is a funny job control
// function available only on OS X and BSD systems
// This lets us use control-Y for yank instead
#ifdef VDSUSP
shell_modes.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif
#endif

// We don't use term_steal because this can fail if fd 0 isn't associated
// with a tty and this function is run regardless of whether stdin is tied
// to a tty. This is harmless in that case. We do it unconditionally
// because disabling ICRNL mode (see above) needs to be done at the
// earliest possible moment. Doing it here means it will be done within
// approximately 1 ms of the start of the shell rather than 250 ms (or
// more) when reader_interactive_init is eventually called.
shell_modes.c_iflag &= ~ICRNL; // disable mapping CR (\cM) to NL (\cJ)
shell_modes.c_iflag &= ~INLCR; // disable mapping NL (\cJ) to CR (\cM)
shell_modes.c_iflag &= ~IXON; // disable flow control
shell_modes.c_iflag &= ~IXOFF; // disable flow control

shell_modes.c_lflag &= ~ICANON; // turn off canonical mode
shell_modes.c_lflag &= ~ECHO; // turn off echo mode
shell_modes.c_lflag &= ~IEXTEN; // turn off handling of discard and lnext characters

shell_modes.c_cc[VMIN] = 1;
shell_modes.c_cc[VTIME] = 0;

// We don't use term_steal because this can fail if fd 0 isn't associated with a tty and this
// function is run regardless of whether stdin is tied to a tty. This is harmless in that case.
// We do it unconditionally because disabling ICRNL mode (see above) needs to be done at the
// earliest possible moment. Doing it here means it will be done within approximately 1 ms of
// the start of the shell rather than 250 ms (or more) when reader_interactive_init is
// eventually called.
//
// TODO: Remove this condition when issue #2315 and #1041 are addressed.
if (is_interactive_session)
Expand Down
20 changes: 20 additions & 0 deletions tests/bind.expect
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,23 @@ expect_prompt -re {\r\nmno pqrt\r\n} {
} unmatched {
puts stderr "emacs transpose words fail, 100ms timeout: long delay"
}

# Verify special characters, such as \cV, are not intercepted by the kernel
# tty driver. Rather, they can be bound and handled by fish.
send "bind \\cV 'echo ctrl-v seen'\r"
expect_prompt
send "\026\r"
expect_prompt -re {ctrl-v seen} {
puts "ctrl-v seen"
} unmatched {
puts stderr "ctrl-v not seen"
}

send "bind \\cO 'echo ctrl-o seen'\r"
expect_prompt
send "\017\r"
expect_prompt -re {ctrl-o seen} {
puts "ctrl-o seen"
} unmatched {
puts stderr "ctrl-o not seen"
}
2 changes: 2 additions & 0 deletions tests/bind.expect.out
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ default-mode custom timeout set correctly
emacs transpose words, 100ms timeout: no delay
emacs transpose words, 100ms timeout: short delay
emacs transpose words, 100ms timeout: long delay
ctrl-v seen
ctrl-o seen

0 comments on commit a243580

Please sign in to comment.