Merge "adb: SIGWINCH support for Windows" am: c1eb5ba0fb

am: 41b04cf896

* commit '41b04cf89677836dba14903226df5c7a3d39adcd':
  adb: SIGWINCH support for Windows
This commit is contained in:
Josh Gao 2016-01-29 22:08:22 +00:00 committed by android-build-merger
commit f74da513c8
3 changed files with 71 additions and 19 deletions

View file

@ -472,18 +472,46 @@ static bool GetFeatureSet(TransportType transport_type, const char* serial, Feat
}
static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
#if !defined(_WIN32)
// Old devices can't handle window size changes.
if (shell == nullptr) return;
#if defined(_WIN32)
struct winsize {
unsigned short ws_row;
unsigned short ws_col;
unsigned short ws_xpixel;
unsigned short ws_ypixel;
};
#endif
winsize ws;
#if defined(_WIN32)
// If stdout is redirected to a non-console, we won't be able to get the
// console size, but that makes sense.
const intptr_t intptr_handle = _get_osfhandle(STDOUT_FILENO);
if (intptr_handle == -1) return;
const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
CONSOLE_SCREEN_BUFFER_INFO info;
memset(&info, 0, sizeof(info));
if (!GetConsoleScreenBufferInfo(handle, &info)) return;
memset(&ws, 0, sizeof(ws));
// The number of visible rows, excluding offscreen scroll-back rows which are in info.dwSize.Y.
ws.ws_row = info.srWindow.Bottom - info.srWindow.Top + 1;
// If the user has disabled "Wrap text output on resize", they can make the screen buffer wider
// than the window, in which case we should use the width of the buffer.
ws.ws_col = info.dwSize.X;
#else
if (ioctl(fd, TIOCGWINSZ, &ws) == -1) return;
#endif
// Send the new window size as human-readable ASCII for debugging convenience.
size_t l = snprintf(shell->data(), shell->data_capacity(), "%dx%d,%dx%d",
ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
shell->Write(ShellProtocol::kIdWindowSizeChange, l + 1);
#endif
}
// Used to pass multiple values to the stdin read thread.
@ -508,7 +536,10 @@ static void* stdin_read_thread_loop(void* x) {
pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
#endif
#if !defined(_WIN32)
#if defined(_WIN32)
// _get_interesting_input_record_uncached() causes unix_read_interruptible()
// to return -1 with errno == EINTR if the window size changes.
#else
// Unblock SIGWINCH for this thread, so our read(2) below will be
// interrupted if the window size changes.
sigset_t mask;
@ -537,20 +568,15 @@ static void* stdin_read_thread_loop(void* x) {
EscapeState state = kStartOfLine;
while (true) {
// Use unix_read() rather than adb_read() for stdin.
D("stdin_read_thread_loop(): pre unix_read(fdi=%d,...)", args->stdin_fd);
#if !defined(_WIN32)
#undef read
int r = read(args->stdin_fd, buffer_ptr, buffer_size);
// Use unix_read_interruptible() rather than adb_read() for stdin.
D("stdin_read_thread_loop(): pre unix_read_interruptible(fdi=%d,...)", args->stdin_fd);
int r = unix_read_interruptible(args->stdin_fd, buffer_ptr,
buffer_size);
if (r == -1 && errno == EINTR) {
send_window_size_change(args->stdin_fd, args->protocol);
continue;
}
#define read ___xxx_read
#else
int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size);
#endif
D("stdin_read_thread_loop(): post unix_read(fdi=%d,...)", args->stdin_fd);
D("stdin_read_thread_loop(): post unix_read_interruptible(fdi=%d,...)", args->stdin_fd);
if (r <= 0) {
// Only devices using the shell protocol know to close subprocess
// stdin. For older devices we want to just leave the connection

View file

@ -164,8 +164,13 @@ static __inline__ int unix_close(int fd)
#undef close
#define close ____xxx_close
// Like unix_read(), but may return EINTR.
extern int unix_read_interruptible(int fd, void* buf, size_t len);
// See the comments for the !defined(_WIN32) version of unix_read().
extern int unix_read(int fd, void* buf, size_t len);
static __inline__ int unix_read(int fd, void* buf, size_t len) {
return TEMP_FAILURE_RETRY(unix_read_interruptible(fd, buf, len));
}
#undef read
#define read ___xxx_read
@ -517,6 +522,11 @@ static __inline__ int adb_read(int fd, void* buf, size_t len)
return TEMP_FAILURE_RETRY( read( fd, buf, len ) );
}
// Like unix_read(), but does not handle EINTR.
static __inline__ int unix_read_interruptible(int fd, void* buf, size_t len) {
return read(fd, buf, len);
}
#undef read
#define read ___xxx_read

View file

@ -2588,6 +2588,18 @@ static bool _get_key_event_record(const HANDLE console, INPUT_RECORD* const inpu
fatal("ReadConsoleInputA did not return one input record");
}
// If the console window is resized, emulate SIGWINCH by breaking out
// of read() with errno == EINTR. Note that there is no event on
// vertical resize because we don't give the console our own custom
// screen buffer (with CreateConsoleScreenBuffer() +
// SetConsoleActiveScreenBuffer()). Instead, we use the default which
// supports scrollback, but doesn't seem to raise an event for vertical
// window resize.
if (input_record->EventType == WINDOW_BUFFER_SIZE_EVENT) {
errno = EINTR;
return false;
}
if ((input_record->EventType == KEY_EVENT) &&
(input_record->Event.KeyEvent.bKeyDown)) {
if (input_record->Event.KeyEvent.wRepeatCount == 0) {
@ -3323,9 +3335,13 @@ void stdin_raw_init() {
// Disable ENABLE_LINE_INPUT so that input is immediately sent.
// Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
// flag also seems necessary to have proper line-ending processing.
if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
ENABLE_LINE_INPUT |
ENABLE_ECHO_INPUT))) {
DWORD new_console_mode = _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
ENABLE_LINE_INPUT |
ENABLE_ECHO_INPUT);
// Enable ENABLE_WINDOW_INPUT to get window resizes.
new_console_mode |= ENABLE_WINDOW_INPUT;
if (!SetConsoleMode(in, new_console_mode)) {
// This really should not fail.
D("stdin_raw_init: SetConsoleMode() failed: %s",
SystemErrorCodeToString(GetLastError()).c_str());
@ -3353,8 +3369,8 @@ void stdin_raw_restore() {
}
}
// Called by 'adb shell' and 'adb exec-in' to read from stdin.
int unix_read(int fd, void* buf, size_t len) {
// Called by 'adb shell' and 'adb exec-in' (via unix_read()) to read from stdin.
int unix_read_interruptible(int fd, void* buf, size_t len) {
if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
// If it is a request to read from stdin, and stdin_raw_init() has been
// called, and it successfully configured the console, then read from