Winsize.txt 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. Programs (such as vi) which automatically adjust to screen resizing are responding to
  2. the SIGWINCH signal, and using a system call to obtain the system's information about
  3. the screen-size. See for example Get width/height of a terminal window in c++?. By the
  4. way, though widely implemented, it does not appear to be documented in POSIX signal.h.
  5. Without taking SIGWINCH into account, a program could ask the terminal about its screensize.
  6. The resize program does this, by sending the terminal control sequences to
  7. move the cursor to the lower-right corner (actually, to row/column 999/999, which is
  8. good enough), and
  9. asking the terminal where the cursor really is.
  10. The behavior of ls and vi (and other programs) regarding ANSI control sequences which would
  11. be embedded in their output depends upon the design of the program. They likely detect
  12. the redirection of their output to a file using the isatty function, and do something
  13. different depending on whether the output is to a terminal, or to a file.
  14. if (write_eintr(STDOUT_FILENO, "\x1b[999C\x1b[999B", len) == len) {
  15. if (get_cursor_pos(wi) == TERM_SUCCESS) {
  16. return TERM_SUCCESS;
  17. }
  18. }
  19. ******************
  20. // Source - https://codereview.stackexchange.com/a/292637
  21. // Posted by Madagascar, modified by community. See post 'Timeline' for change history
  22. // Retrieved 2026-05-28, License - CC BY-SA 4.0
  23. #undef _POSIX_C_SOURCE
  24. #undef _XOPEN_SOURCE
  25. #define _POSIX_C_SOURCE 200819L
  26. #define _XOPEN_SOURCE 700
  27. #include <errno.h>
  28. #include <limits.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <sys/ioctl.h>
  33. #include <termios.h>
  34. #include <unistd.h>
  35. #include "io.h"
  36. #include "sequences.h"
  37. #include "term.h"
  38. static bool parse_long(const char s[static 1], int base, long val[static 1])
  39. {
  40. char *endptr;
  41. errno = 0;
  42. const long i = strtol(s, &endptr, base);
  43. if (endptr == s || *endptr != '\0' || errno != 0) {
  44. return false;
  45. }
  46. *val = i;
  47. return true;
  48. }
  49. static TermCodes get_cursor_pos(WinInfo wi[static 1])
  50. {
  51. /* The cursor is positioned at the bottom right of the window. We can learn
  52. * how many rows and columns there must be on the screen by querying the
  53. * position of the cursor. */
  54. ssize_t len = (ssize_t) strlen("\x1b[6n");
  55. if (write_eintr(STDOUT_FILENO, "\x1b[6n", len) != len) {
  56. return TERM_FAILURE;
  57. }
  58. /* The reply is an escape sequence of the form '\x1b[24;80R', we will
  59. * read it into a buffer until read() returns EOF or until we get to
  60. * the 'R' character. */
  61. char buf[32]; /* Should be more than enough. */
  62. for (size_t i = 0; i < sizeof buf - 1; ++i) {
  63. if (read_eintr(STDIN_FILENO, &buf[i], 1) != 1 || buf[i] == 'R') {
  64. break;
  65. }
  66. }
  67. *buf = '\0';
  68. /* Skip the escape character and the left square brace. */
  69. return memcmp(buf, "\x1b[", 2) != 0
  70. || sscanf(&buf[2], "%u;%u", &wi->rows, &wi->cols) != 2
  71. ? TERM_FAILURE
  72. : TERM_SUCCESS;
  73. }
  74. TermCodes term_get_winsize(WinInfo wi[static 1])
  75. {
  76. #if defined(TIOCGWINSZ)
  77. struct winsize ws;
  78. if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) {
  79. wi->rows = ws.ws_row;
  80. wi->cols = ws.ws_col;
  81. return TERM_SUCCESS;
  82. }
  83. #elif defined(TIOCGSIZE)
  84. struct ttysize ts;
  85. if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) == 0) {
  86. wi->rows = ts.ts_row;
  87. wi->cols = ts.ts_col;
  88. return TERM_SUCCESS;
  89. }
  90. #endif /* defined(TIOCGWINSZ) */
  91. /* ioctl() failed. Fallback to VT100/ANSI escape sequences. */
  92. ssize_t len = (ssize_t) strlen("\x1b[999C\x1b[999B");
  93. if (write_eintr(STDOUT_FILENO, "\x1b[999C\x1b[999B", len) == len) {
  94. if (get_cursor_pos(wi) == TERM_SUCCESS) {
  95. return TERM_SUCCESS;
  96. }
  97. }
  98. /* write() or get_cursor_pos() failed as well. Now as a last resort, check
  99. * LINES and COLUMNS environment variables.
  100. * Though note that these variables are not reliable, are not guaranteed
  101. * to exist, and might not be up to date if the user changes the terminal
  102. * size. If set, the sh, ash, dash, csh shells do not update LINES and
  103. * COLUMNS, but bash, fish, zsh, ksh, ksh93u+m, and tcsh handle SIGWINCH to
  104. * update these variables. */
  105. const char *const rows = getenv("LINES");
  106. const char *const cols = getenv("COLUMNS");
  107. if (rows != nullptr && cols != nullptr) {
  108. long r;
  109. long c;
  110. bool res = parse_long(rows, 10, &r) && parse_long(cols, 10, &c);
  111. if (!res || r > UINT_MAX || c > UINT_MAX) {
  112. return TERM_FAILURE;
  113. }
  114. wi->rows = (unsigned int) r;
  115. wi->cols = (unsigned int) c;
  116. return TERM_SUCCESS;
  117. }
  118. return TERM_FAILURE;
  119. }