Read ANSI device status report response escape sequence

I am trying to get cursor coordinates in VT100 terminal using the following code:

void getCursor(int* x, int* y) { printf("\033[6n"); scanf("\033[%d;%dR", x, y); } 

I use the following ANSI escape sequence:

Device Status Report - ESC [6n

Sends the cursor position to the application as (as if typed on the keyboard) ESC [n; mR, where n is the row and m is the column.

The code compilation and the ANSI sequence are sent, but after receiving it, the terminal prints the string ^[[x;yR to stdout instead of stdin , which makes it impossible to extract it from the program:

terminal window

Obviously, the line is for the program, so I have to do something wrong. Does anyone know what it is?

+8
source share
4 answers

Your program is running but waiting for the EOL symbol.

scanf is line-oriented, so it expects a new line before processing. Try running your program, and then press enter.

The solution is to use something else that does not require a new line to read input, and then use sscanf to parse the values.

You will also need to make stdin non-blocking, or you will not receive input until the buffer is full or stdin is closed. See This Question Creating a stdin Lock

You should also call fflush(stdout); after your printf to make sure that it is actually written (printf often runs with line buffering, so without a new line it might not clear the buffer).

+3
source

I ask you to indicate the position of the cursor. If I don't have a response after 100 ms (this is arbitrary), I assume that the console is not ansi.

 /* This function tries to get the position of the cursor on the terminal. It can also be used to detect if the terminal is ANSI. Return 1 in case of success, 0 otherwise.*/ int console_try_to_get_cursor_position(int* x, int *y) { fd_set readset; int success = 0; struct timeval time; struct termios term, initial_term; /*We store the actual properties of the input console and set it as: no buffered (~ICANON): avoid blocking no echoing (~ECHO): do not display the result on the console*/ tcgetattr(STDIN_FILENO, &initial_term); term = initial_term; term.c_lflag &=~ICANON; term.c_lflag &=~ECHO; tcsetattr(STDIN_FILENO, TCSANOW, &term); //We request position print_escape_command("6n"); fflush(stdout); //We wait 100ms for a terminal answer FD_ZERO(&readset); FD_SET(STDIN_FILENO, &readset); time.tv_sec = 0; time.tv_usec = 100000; //If it success we try to read the cursor value if (select(STDIN_FILENO + 1, &readset, NULL, NULL, &time) == 1) if (scanf("\033[%d;%dR", x, y) == 2) success = 1; //We set back the properties of the terminal tcsetattr(STDIN_FILENO, TCSADRAIN, &initial_term); return success; } 
+4
source

I believe that you really get the expected answer in stdin. But imagine what really happens:

  • you send the request as an escape sequence to stdout
  • the terminal receives it and formulates the corresponding response as an escape sequence, as well as
  • the response is sent to stdin
  • scanf is called and stdin is redirected through the shell, where the readline library is used for interactive and editable user input.
  • readline captures an escape sequence rather than passing it to the terminal
  • readline re-formulates it without an ESC character to prevent the check sequence from occurring, and makes it readable only using printed characters
  • quirked answer reaches scanf but its too late
  • quirked answer also reflects on stdout so the user can instantly see what she typed.

To avoid this, use the getc() loop ( ==fgetc(stdin) ) instead. If you encounter ESC ( 0x1B ) than a dump, enter the following characters in the string until you find the final separator for the ESC sequence (in your case 'n' ). After that you can use sscanf(esqString, formatString, ...) .

But before you run into a loop, you need to change using termios in raw mode (see code example below). Otherwise, nothing would have changed.

0
source
 #include <stdlib.h> #include <stdio.h> #include <conio.h> void readCoord(void* row, void* col){ int i = 0; char* input = malloc(10); printf("\e[6n"); while(!_kbhit()) _sleep(1); while(_kbhit()) *(input + (i++)) = getch(); *(input + i) = '\0'; sscanf(input, "\e[%d;%dR", row, col); } void main(void){ int i = 0, r, c; char* coord = malloc(10); printf("Hello"); readCoord(&r , &c); printf("\nYour coordinate is (%d, %d)", c, r); } 

_kbhit() used to detect input (DSR is processed as if it is getch() on the keyboard), and getch() to read and remove a character from standard input.

This program uses conio.h , which is not a standard and therefore not recommended for portable C programs.

0
source

All Articles