summaryrefslogtreecommitdiffstats
path: root/term-utils/setterm.c
diff options
context:
space:
mode:
authorSami Kerola2016-12-29 11:28:54 +0100
committerSami Kerola2016-12-29 11:56:15 +0100
commit5d795999511adc455d223be8281948796bd7d087 (patch)
treecf2d48eb177e717343f9926d9c6b9858f578d98c /term-utils/setterm.c
parentMerge branch 'getrandom' of git://github.com/kerolasa/lelux-utiliteetit (diff)
downloadkernel-qcow2-util-linux-5d795999511adc455d223be8281948796bd7d087.tar.gz
kernel-qcow2-util-linux-5d795999511adc455d223be8281948796bd7d087.tar.xz
kernel-qcow2-util-linux-5d795999511adc455d223be8281948796bd7d087.zip
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 <kilobyte@angband.pl> Signed-off-by: Sami Kerola <kerolasa@iki.fi>
Diffstat (limited to 'term-utils/setterm.c')
-rw-r--r--term-utils/setterm.c112
1 files changed, 111 insertions, 1 deletions
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 <linux/tiocl.h>
#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 <terminal_name> 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"));