aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Kurtis Rader <krader@skepticism.us>2016-06-17 13:08:25 -0700
committerGravatar Kurtis Rader <krader@skepticism.us>2016-06-18 19:33:54 -0700
commit8e21d5de92c4c85f55bbef34b6ffadd40a37e3db (patch)
tree73194959e176a000dd2e73427cab486c2a76cbd0
parentf04644f749f54a9773884a61e76540c53bf0d42b (diff)
deal with broken ttys on MS Windows
The tty device timestamps on MS Windows aren't usable because they're always the current time. So fish can't use them to decide if the entire prompt needs to be repainted. Fixes #2859
-rw-r--r--src/common.cpp2
-rw-r--r--src/common.h11
-rw-r--r--src/fish.cpp31
-rw-r--r--src/screen.cpp10
4 files changed, 52 insertions, 2 deletions
diff --git a/src/common.cpp b/src/common.cpp
index fcbb2465..b0e96789 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -71,6 +71,8 @@ static rwlock_t termsize_rwlock;
static char *wcs2str_internal(const wchar_t *in, char *out);
static void debug_shared(const wchar_t msg_level, const wcstring &msg);
+bool has_working_tty_timestamps = true;
+
#ifdef HAVE_BACKTRACE_SYMBOLS
// This function produces a stack backtrace with demangled function & method names. It is based on
// https://gist.github.com/fmela/591333 but adapted to the style of the fish project.
diff --git a/src/common.h b/src/common.h
index b8791279..0c1b77d4 100644
--- a/src/common.h
+++ b/src/common.h
@@ -21,6 +21,13 @@
#include "fallback.h" // IWYU pragma: keep
#include "signal.h" // IWYU pragma: keep
+// Define a symbol we can use elsewhere in our code to determine if we're being built on MS Windows
+// under Cygwin.
+#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(__CYGWIN__) || \
+ defined(__WIN32__)
+#define OS_IS_CYGWIN
+#endif
+
/// Avoid writing the type name twice in a common "static_cast-initialization". Caveat: This doesn't
/// work with type names containing commas!
#define CAST_INIT(type, dst, src) type dst = static_cast<type>(src)
@@ -188,6 +195,10 @@ extern const wchar_t *program_name;
void read_ignore(int fd, void *buff, size_t count);
void write_ignore(int fd, const void *buff, size_t count);
+/// Set to false at run-time if it's been determined we can't trust the last modified timestamp on
+/// the tty.
+extern bool has_working_tty_timestamps;
+
/// This macro is used to check that an input argument is not null. It is a bit lika a non-fatal
/// form of assert. Instead of exit-ing on failure, the current function is ended at once. The
/// second parameter is the return value of the current function on failure.
diff --git a/src/fish.cpp b/src/fish.cpp
index 45899866..9482bd3f 100644
--- a/src/fish.cpp
+++ b/src/fish.cpp
@@ -407,6 +407,36 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
return optind;
}
+/// Various things we need to initialize at run-time that don't really fit any of the other init
+/// routines.
+static void misc_init() {
+#ifdef OS_IS_CYGWIN
+ // MS Windows tty devices do not have either a read or write timestamp. Those respective fields
+ // of `struct stat` are always the current time. Which means we can't use them. So we assume no
+ // external program has written to the terminal behind our back. This makes multiline prompts
+ // usable. See issue #2859.
+ has_working_tty_timestamps = false;
+#else
+ // This covers Windows Subsystem for Linux (WSL).
+ int fd = open("/proc/sys/kernel/osrelease", O_RDONLY);
+ if (fd != -1) {
+ const char *magic_suffix = "Microsoft";
+ int len_magic_suffix = strlen(magic_suffix);
+ char osrelease[128] = {0};
+ int len_osrelease = read(fd, osrelease, sizeof(osrelease) - 1);
+ if (osrelease[len_osrelease - 1] == '\n') {
+ osrelease[len_osrelease - 1] = '\0';
+ len_osrelease--;
+ }
+ if (len_osrelease >= len_magic_suffix &&
+ strcmp(magic_suffix, osrelease + len_osrelease - len_magic_suffix) == 0) {
+ has_working_tty_timestamps = false;
+ }
+ close(fd);
+ }
+#endif // OS_IS_MS_WINDOWS
+}
+
int main(int argc, char **argv) {
int res = 1;
int my_optind = 0;
@@ -452,6 +482,7 @@ int main(int argc, char **argv) {
history_init();
// For set_color to support term256 in config.fish (issue #1022).
update_fish_color_support();
+ misc_init();
parser_t &parser = parser_t::principal_parser();
diff --git a/src/screen.cpp b/src/screen.cpp
index fc5c927c..a9d9ddcf 100644
--- a/src/screen.cpp
+++ b/src/screen.cpp
@@ -347,12 +347,18 @@ static void s_save_status(screen_t *s) {
static void s_check_status(screen_t *s) {
fflush(stdout);
fflush(stderr);
+ if (!has_working_tty_timestamps) {
+ // We can't reliably determine if the terminal has been written to behind our back so we
+ // just assume that hasn't happened and hope for the best. This is important for multi-line
+ // prompts to work correctly.
+ return;
+ }
fstat(1, &s->post_buff_1);
fstat(2, &s->post_buff_2);
- int changed = (s->prev_buff_1.st_mtime != s->post_buff_1.st_mtime) ||
- (s->prev_buff_2.st_mtime != s->post_buff_2.st_mtime);
+ bool changed = (s->prev_buff_1.st_mtime != s->post_buff_1.st_mtime) ||
+ (s->prev_buff_2.st_mtime != s->post_buff_2.st_mtime);
#if defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
changed = changed ||