| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- Programs (such as vi) which automatically adjust to screen resizing are responding to
- the SIGWINCH signal, and using a system call to obtain the system's information about
- the screen-size. See for example Get width/height of a terminal window in c++?. By the
- way, though widely implemented, it does not appear to be documented in POSIX signal.h.
- Without taking SIGWINCH into account, a program could ask the terminal about its screensize.
- The resize program does this, by sending the terminal control sequences to
- move the cursor to the lower-right corner (actually, to row/column 999/999, which is
- good enough), and
- asking the terminal where the cursor really is.
- The behavior of ls and vi (and other programs) regarding ANSI control sequences which would
- be embedded in their output depends upon the design of the program. They likely detect
- the redirection of their output to a file using the isatty function, and do something
- different depending on whether the output is to a terminal, or to a file.
- if (write_eintr(STDOUT_FILENO, "\x1b[999C\x1b[999B", len) == len) {
- if (get_cursor_pos(wi) == TERM_SUCCESS) {
- return TERM_SUCCESS;
- }
- }
- ******************
- // Source - https://codereview.stackexchange.com/a/292637
- // Posted by Madagascar, modified by community. See post 'Timeline' for change history
- // Retrieved 2026-05-28, License - CC BY-SA 4.0
- #undef _POSIX_C_SOURCE
- #undef _XOPEN_SOURCE
- #define _POSIX_C_SOURCE 200819L
- #define _XOPEN_SOURCE 700
- #include <errno.h>
- #include <limits.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/ioctl.h>
- #include <termios.h>
- #include <unistd.h>
- #include "io.h"
- #include "sequences.h"
- #include "term.h"
- static bool parse_long(const char s[static 1], int base, long val[static 1])
- {
- char *endptr;
- errno = 0;
- const long i = strtol(s, &endptr, base);
-
- if (endptr == s || *endptr != '\0' || errno != 0) {
- return false;
- }
- *val = i;
- return true;
- }
- static TermCodes get_cursor_pos(WinInfo wi[static 1])
- {
- /* The cursor is positioned at the bottom right of the window. We can learn
- * how many rows and columns there must be on the screen by querying the
- * position of the cursor. */
- ssize_t len = (ssize_t) strlen("\x1b[6n");
- if (write_eintr(STDOUT_FILENO, "\x1b[6n", len) != len) {
- return TERM_FAILURE;
- }
- /* The reply is an escape sequence of the form '\x1b[24;80R', we will
- * read it into a buffer until read() returns EOF or until we get to
- * the 'R' character. */
- char buf[32]; /* Should be more than enough. */
- for (size_t i = 0; i < sizeof buf - 1; ++i) {
- if (read_eintr(STDIN_FILENO, &buf[i], 1) != 1 || buf[i] == 'R') {
- break;
- }
- }
- *buf = '\0';
- /* Skip the escape character and the left square brace. */
- return memcmp(buf, "\x1b[", 2) != 0
- || sscanf(&buf[2], "%u;%u", &wi->rows, &wi->cols) != 2
- ? TERM_FAILURE
- : TERM_SUCCESS;
- }
- TermCodes term_get_winsize(WinInfo wi[static 1])
- {
- #if defined(TIOCGWINSZ)
- struct winsize ws;
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
- wi->rows = ws.ws_row;
- wi->cols = ws.ws_col;
- return TERM_SUCCESS;
- }
- #elif defined(TIOCGSIZE)
- struct ttysize ts;
- if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) == 0) {
- wi->rows = ts.ts_row;
- wi->cols = ts.ts_col;
- return TERM_SUCCESS;
- }
- #endif /* defined(TIOCGWINSZ) */
- /* ioctl() failed. Fallback to VT100/ANSI escape sequences. */
- ssize_t len = (ssize_t) strlen("\x1b[999C\x1b[999B");
- if (write_eintr(STDOUT_FILENO, "\x1b[999C\x1b[999B", len) == len) {
- if (get_cursor_pos(wi) == TERM_SUCCESS) {
- return TERM_SUCCESS;
- }
- }
-
- /* write() or get_cursor_pos() failed as well. Now as a last resort, check
- * LINES and COLUMNS environment variables.
- * Though note that these variables are not reliable, are not guaranteed
- * to exist, and might not be up to date if the user changes the terminal
- * size. If set, the sh, ash, dash, csh shells do not update LINES and
- * COLUMNS, but bash, fish, zsh, ksh, ksh93u+m, and tcsh handle SIGWINCH to
- * update these variables. */
- const char *const rows = getenv("LINES");
- const char *const cols = getenv("COLUMNS");
- if (rows != nullptr && cols != nullptr) {
- long r;
- long c;
- bool res = parse_long(rows, 10, &r) && parse_long(cols, 10, &c);
-
- if (!res || r > UINT_MAX || c > UINT_MAX) {
- return TERM_FAILURE;
- }
- wi->rows = (unsigned int) r;
- wi->cols = (unsigned int) c;
- return TERM_SUCCESS;
- }
- return TERM_FAILURE;
- }
|