While researching this problem, in the comments I found someone mentioning ANSI escape codes to get the terminal size. Since I will be using ANSI escape sequences, I thought this would be a much more elegant way to get the terminal size than ioctl()
or getenv()
.
Here is a very good post about ioctl(), and this is the comment that piqued my interest:
Just stumbled upon this answer, and my jaw dropped when I realized that getenv("COLUMNS") works perfectly when running under watch(1). So now I have a set of fallbacks, all from the TIOCWINSZ ioctl, to getenv if not a tty, down to the classic ANSI escape "move cursor to 9999,9999 and the query cursor pos. The last one works on serial consoles for embedded systems :)
Now, I did find some posts (1, 2) about ANSI, but none answer the specific question I have - how to actually do it?
Using this chart, I deduced that in order to move the cursor to the extreme right and down position, I would need the CUP
(CSI n ; m H
) "Cursor Position" command, which would probably translate to something like \x1b[9999;9999H
. This is the easy part since it is a command.
The second part is where I have a big problem. Even if I could deduce that I need the DSR
(CSI 6n
) "Device Status Report" command, and that it goes \x1b[6n
, how does this querying work, i.e., how can I take the data and store it in a variable, preferrably without disrupting the other data that is being displayed on the terminal?
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <ctype.h>
#define SIZE 100
int main ( void) {
char in[SIZE] = "";
int each = 0;
int ch = 0;
int rows = 0;
int cols = 0;
struct termios original, changed;
// change terminal settings
tcgetattr( STDIN_FILENO, &original);
changed = original;
changed.c_lflag &= ~( ICANON | ECHO);
changed.c_cc[VMIN] = 1;
changed.c_cc[VTIME] = 0;
tcsetattr( STDIN_FILENO, TCSANOW, &changed);
printf ( "\033[2J"); //clear screen
printf ( "\033[9999;9999H"); // cursor should move as far as it can
printf ( "\033[6n"); // ask for cursor position
while ( ( ch = getchar ()) != 'R') { // R terminates the response
if ( EOF == ch) {
break;
}
if ( isprint ( ch)) {
if ( each + 1 < SIZE) {
in[each] = ch;
each++;
in[each] = '\0';
}
}
}
printf ( "\033[1;1H"); // move to upper left corner
if ( 2 == sscanf ( in, "[%d;%d", &rows, &cols)) {
printf ( "%d rows\n", rows);
printf ( "%d cols\n", cols);
}
// restore terminal settings
tcsetattr( STDIN_FILENO, TCSANOW, &original);
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With