From 5d795999511adc455d223be8281948796bd7d087 Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Thu, 29 Dec 2016 10:28:54 +0000 Subject: setterm: add --resize option Reset terminal size by assessing maximum row and column. This is useful when actual geometry and kernel terminal driver are not in sync. Addresses: http://bugs.debian.org/835636 Based-on-work-by: Adam Borowski Signed-off-by: Sami Kerola --- term-utils/setterm.1 | 7 ++++ term-utils/setterm.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) (limited to 'term-utils') diff --git a/term-utils/setterm.1 b/term-utils/setterm.1 index 2a06c1a08..22a8ff03a 100644 --- a/term-utils/setterm.1 +++ b/term-utils/setterm.1 @@ -228,6 +228,13 @@ Turns keyboard repeat on or off. Displays the terminal reset string, which typically resets the terminal to its power-on state. .TP +\fB\-\-resize\fP +Reset terminal size by assessing maximum row and column. This is useful +when actual geometry and kernel terminal driver are not in sync. Most +notable use case is with serial consoles, that do not use +.BR ioctl (3) +but just byte streams and breaks. +.TP \fB\-\-reverse\fP [\fBon\fP|\fBoff\fP] Turns reverse video mode on or off. Except on a virtual console, .B \-\-reverse off diff --git a/term-utils/setterm.c b/term-utils/setterm.c index abcf8b291..900a9f3b1 100644 --- a/term-utils/setterm.c +++ b/term-utils/setterm.c @@ -75,6 +75,7 @@ # include #endif +#include "all-io.h" #include "c.h" #include "closestream.h" #include "nls.h" @@ -182,7 +183,7 @@ struct setterm_control { opt_appck_on:1, opt_invsc_on:1, opt_msg_on:1, opt_cl_all:1, vcterm:1; /* Option flags. Set when an option is invoked. */ - uint64_t opt_term:1, opt_reset:1, opt_initialize:1, opt_cursor:1, + uint64_t opt_term:1, opt_reset:1, opt_resize:1, opt_initialize:1, opt_cursor:1, opt_linewrap:1, opt_default:1, opt_foreground:1, opt_background:1, opt_bold:1, opt_blink:1, opt_reverse:1, opt_underline:1, opt_store:1, opt_clear:1, opt_blank:1, @@ -385,6 +386,7 @@ static void __attribute__((__noreturn__)) usage(FILE *out) fputs(USAGE_OPTIONS, out); fputs(_(" --term override TERM environment variable\n"), out); fputs(_(" --reset reset terminal to power-on state\n"), out); + fputs(_(" --resize reset terminal rows and columns\n"), out); fputs(_(" --initialize display init string, and use default settings\n"), out); fputs(_(" --default use default terminal settings\n"), out); fputs(_(" --store save current terminal settings as default\n"), out); @@ -437,6 +439,7 @@ static void parse_option(struct setterm_control *ctl, int ac, char **av) enum { OPT_TERM = CHAR_MAX + 1, OPT_RESET, + OPT_RESIZE, OPT_INITIALIZE, OPT_CURSOR, OPT_REPEAT, @@ -474,6 +477,7 @@ static void parse_option(struct setterm_control *ctl, int ac, char **av) static const struct option longopts[] = { {"term", required_argument, NULL, OPT_TERM}, {"reset", no_argument, NULL, OPT_RESET}, + {"resize", no_argument, NULL, OPT_RESIZE}, {"initialize", no_argument, NULL, OPT_INITIALIZE}, {"cursor", required_argument, NULL, OPT_CURSOR}, {"repeat", required_argument, NULL, OPT_REPEAT}, @@ -527,6 +531,9 @@ static void parse_option(struct setterm_control *ctl, int ac, char **av) case OPT_RESET: ctl->opt_reset = set_opt_flag(ctl->opt_reset); break; + case OPT_RESIZE: + ctl->opt_resize = set_opt_flag(ctl->opt_resize); + break; case OPT_INITIALIZE: ctl->opt_initialize = set_opt_flag(ctl->opt_initialize); break; @@ -815,6 +822,104 @@ static int vc_only(struct setterm_control *ctl, const char *err) return ctl->vcterm; } +static void tty_raw(struct termios *saved_attributes, int *saved_fl) +{ + struct termios tattr; + + fcntl(STDIN_FILENO, F_GETFL, saved_fl); + tcgetattr(STDIN_FILENO, saved_attributes); + fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK); + memcpy(&tattr, saved_attributes, sizeof(struct termios)); + tattr.c_lflag &= ~(ICANON | ECHO); + tattr.c_cc[VMIN] = 1; + tattr.c_cc[VTIME] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr); +} + +static void tty_restore(struct termios *saved_attributes, int *saved_fl) +{ + fcntl(STDIN_FILENO, F_SETFL, *saved_fl); + tcsetattr(STDIN_FILENO, TCSANOW, saved_attributes); +} + +static int select_wait(void) +{ + struct timeval tv; + fd_set set; + int ret; + + FD_ZERO(&set); + FD_SET(STDIN_FILENO, &set); + tv.tv_sec = 10; + tv.tv_usec = 0; + while ((ret = select(1, &set, NULL, NULL, &tv)) < 0) { + if (errno == EINTR) + continue; + err(EXIT_FAILURE, _("select failed")); + } + return ret; +} + +static int resizetty(void) +{ + /* + * \e7 Save current state (cursor coordinates, attributes, + * character sets pointed at by G0, G1). + * \e[r Set scrolling region; parameters are top and bottom row. + * \e[32766E Move cursor down 32766 (INT16_MAX - 1) rows. + * \e[32766C Move cursor right 32766 columns. + * \e[6n Report cursor position. + * \e8 Restore state most recently saved by \e7. + */ + static const char *getpos = "\e7\e[r\e[32766E\e[32766C\e[6n\e8"; + char retstr[32]; + int row, col; + size_t pos; + ssize_t rc; + struct winsize ws; + struct termios saved_attributes; + int saved_fl; + + if (!isatty(STDIN_FILENO)) + errx(EXIT_FAILURE, _("stdin does not refer to a terminal")); + + tty_raw(&saved_attributes, &saved_fl); + if (write_all(STDIN_FILENO, getpos, strlen(getpos)) < 0) { + warn(_("write failed")); + tty_restore(&saved_attributes, &saved_fl); + return 1; + } + for (pos = 0; pos < sizeof(retstr) - 1;) { + if (0 == select_wait()) + break; + if ((rc = + read(STDIN_FILENO, retstr + pos, + sizeof(retstr) - 1 - pos)) < 0) { + if (errno == EINTR) + continue; + warn(_("read failed")); + tty_restore(&saved_attributes, &saved_fl); + return 1; + } + pos += rc; + if (retstr[pos - 1] == 'R') + break; + } + retstr[pos] = 0; + tty_restore(&saved_attributes, &saved_fl); + rc = sscanf(retstr, "\033[%d;%dR", &row, &col); + if (rc != 2) { + warnx(_("invalid cursor position: %s"), retstr); + return 1; + } + memset(&ws, 0, sizeof(struct winsize)); + ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); + ws.ws_row = row; + ws.ws_col = col; + ioctl(STDIN_FILENO, TIOCSWINSZ, &ws); + return 0; +} + static void perform_sequence(struct setterm_control *ctl) { int result; @@ -823,6 +928,11 @@ static void perform_sequence(struct setterm_control *ctl) if (ctl->opt_reset) putp(ti_entry("rs1")); + /* -resize. */ + if (ctl->opt_resize) + if (resizetty()) + warnx(_("reset failed")); + /* -initialize. */ if (ctl->opt_initialize) putp(ti_entry("is2")); -- cgit v1.2.3-55-g7522