Date: Tue, 15 Sep 2020 06:15:34 +0200 Subject: [PATCH] notify window size change via escaped inband information (Biswa96/wslbridge2#21, mintty/wsltty#220) Co-authored-by: Thomas Wolff --- src/wslbridge2-backend.cpp | 87 +++++++++++++++++++++++++++++++++++++- src/wslbridge2.cpp | 83 ++++++++++++++++++++++++++++++------ 2 files changed, 155 insertions(+), 15 deletions(-) diff --git a/src/wslbridge2-backend.cpp b/src/wslbridge2-backend.cpp index b50ee9c..ad429ae 100644 --- a/src/wslbridge2-backend.cpp +++ b/src/wslbridge2-backend.cpp @@ -24,6 +24,7 @@ #include #include #include +#include // PIPE_BUF #include #include @@ -315,6 +316,9 @@ int main(int argc, char *argv[]) /* Use dupped master fd to read OR write */ const int mfd_dp = dup(mfd); assert(mfd_dp > 0); +#ifdef debug_to_input + FILE * debug = fdopen(mfd_dp, "w"); // for fprintf +#endif sigset_t set; sigemptyset(&set); @@ -333,6 +337,7 @@ int main(int argc, char *argv[]) ssize_t readRet = 0, writeRet = 0; char data[1024]; /* Buffer to hold raw data from pty */ + assert(sizeof data <= PIPE_BUF); do { @@ -343,8 +348,86 @@ int main(int argc, char *argv[]) if (fds[0].revents & POLLIN) { readRet = recv(ioSockets.inputSock, data, sizeof data, 0); - if (readRet > 0) - writeRet = write(mfd_dp, data, readRet); + + char * s = data; + int len = readRet; + writeRet = 1; + while (writeRet > 0 && len > 0) + { + if (!*s) + { + // dispatch NUL escaped inband information + s++; + len--; + + if (len < 9 && s + 9 >= data + sizeof data) + { + // make room for additional loading + memcpy(data, s, len); + s = data; + } + + // ensure 1 more byte is loaded to dispatch on + if (!len) + { + readRet = recv(ioSockets.inputSock, s, 1, 0); + if (readRet > 0) + { + len += readRet; + } + else + { + writeRet = -1; + break; + } + } + if (*s == 2) + { + // STX: escaped NUL + s++; + len--; + writeRet = write(mfd_dp, "", 1); + } + else if (*s == 16) + { + // DLE: terminal window size change + s++; + len--; + // ensure 8 more bytes are loaded for winsize + while (readRet > 0 && len < 8) + { + readRet = recv(ioSockets.inputSock, s + len, 8 - len, 0); + if (readRet > 0) + { + len += readRet; + } + } + if (readRet <= 0) + { + writeRet = -1; + break; + } + struct winsize * winsp = (struct winsize *)s; + s += 8; + len -= 8; + winsp->ws_xpixel = 0; + winsp->ws_ypixel = 0; + ret = ioctl(mfd, TIOCSWINSZ, winsp); + if (ret != 0) + perror("ioctl(TIOCSWINSZ)"); + } + } + else + { + int n = strnlen(s, len); + writeRet = write(mfd_dp, s, n); + if (writeRet > 0) + { + s += writeRet; + len -= writeRet; + } + } + } } /* Resize window when buffer received in control socket */ diff --git a/src/wslbridge2.cpp b/src/wslbridge2.cpp index 75ccb1b..e9fbbf7 100644 --- a/src/wslbridge2.cpp +++ b/src/wslbridge2.cpp @@ -56,32 +56,85 @@ union IoSockets /* global variable */ static union IoSockets g_ioSockets = { 0, 0, 0 }; + +#define dont_debug_inband +#define dont_use_controlsocket + static void resize_window(int signum) { +#ifdef use_controlsocket +#warning this may crash for unknown reason, maybe terminate the backend struct winsize winp; + ioctl(STDIN_FILENO, TIOCGWINSZ, &winp); /* Send terminal window size to control socket */ - ioctl(STDIN_FILENO, TIOCGWINSZ, &winp); send(g_ioSockets.controlSock, &winp, sizeof winp, 0); +#else + static char wins[2 + sizeof(struct winsize)] = {0, 16}; + static struct winsize * winsp = (struct winsize *)&wins[2]; + ioctl(STDIN_FILENO, TIOCGWINSZ, winsp); + +#ifdef debug_inband + /* Send terminal window size inband, visualized as ESC sequence */ + char resizesc[55]; + //sprintf(resizesc, "\e_8;%u;%u\a", winsp->ws_row, winsp->ws_col); + sprintf(resizesc, "^[_8;%u;%u^G", winsp->ws_row, winsp->ws_col); + send(g_ioSockets.inputSock, resizesc, strlen(resizesc), 0); +#else + /* Send terminal window size inband, with NUL escape */ + send(g_ioSockets.inputSock, wins, sizeof wins, 0); +#endif +#endif } static void* send_buffer(void *param) { int ret; char data[1024]; - - struct pollfd fds = { STDIN_FILENO, POLLIN, 0 }; + assert(sizeof data <= PIPE_BUF); while (1) { +#ifdef use_poll + // we could poll on a single channel but we don't need to + static struct pollfd fds = { STDIN_FILENO, POLLIN, 0 }; ret = poll(&fds, 1, -1); if (fds.revents & POLLIN) +#else + if (1) +#endif { ret = read(STDIN_FILENO, data, sizeof data); - if (ret > 0) - ret = send(g_ioSockets.inputSock, data, ret, 0); - else + + char * s = data; + int len = ret; + while (ret > 0 && len > 0) + { + if (!*s) + { + // send NUL STX +#ifdef debug_inband + ret = send(g_ioSockets.inputSock, (void*)"nul", 3, 0); +#else + static char NUL_STX[] = {0, 2}; + ret = send(g_ioSockets.inputSock, NUL_STX, 2, 0); +#endif + s++; + len--; + } + else + { + int n = strnlen(s, len); + ret = send(g_ioSockets.inputSock, s, n, 0); + if (ret > 0) + { + s += ret; + len -= ret; + } + } + } + if (ret <= 0) break; } } @@ -537,13 +590,6 @@ int main(int argc, char *argv[]) closesocket(controlSocket); } - /* Create thread to send window size through control socket */ - struct sigaction act = {}; - act.sa_handler = resize_window; - act.sa_flags = SA_RESTART; - ret = sigaction(SIGWINCH, &act, NULL); - assert(ret == 0); - /* Create thread to send input buffer to input socket */ pthread_t tidInput; ret = pthread_create(&tidInput, nullptr, send_buffer, nullptr); @@ -556,6 +602,17 @@ int main(int argc, char *argv[]) termState.enterRawMode(); + /* Create thread to send window size through control socket */ + struct sigaction act = {}; + act.sa_handler = resize_window; + act.sa_flags = SA_RESTART; + ret = sigaction(SIGWINCH, &act, NULL); + assert(ret == 0); + + /* Notify initial size in case it's changed since starting */ + //resize_window(0); + kill(getpid(), SIGWINCH); + /* * wsltty#254: WORKAROUND: Terminates input thread forcefully * when output thread exits. Need some inter-thread syncing.