How to get cursor position in C using ANSI code

后端 未结 2 810
無奈伤痛
無奈伤痛 2021-01-15 04:47

I am trying to get cursor position from a little c program so after googling I found this ANSI code \\x1b[6n. It should return x and y location of cursor ( if I

相关标签:
2条回答
  • 2021-01-15 05:30

    On some terminals, such as DEC VT102 and later VTs, and on many terminal emulators, notably XTerm and its many imitations, sending Esc [ 6 n will make the terminal respond with Esc [ row ; column R, where row and column are decimal representations of the position of the text cursor.

    So your terminal emulator is not replying with ;1R; it is replying correctly, but the readline routines are eating Esc [ and the decimal digits up to ; (and flash the screen or beep the bell, depending on configuration).

    Here is a nice Bash command to illustrate:

    out=''; \
    echo $'\e[6n'; \
    while read -n 1 -s -t 1; do out="$out$REPLY"; done < /dev/tty; \
    echo -n "$out" | od -A x -t x1z -v
    

    Running this gives:

    $ out=''; \
    > echo $'\e[6n'; \
    > while read -n 1 -s -t 1; do out="$out$REPLY"; done < /dev/tty; \
    > echo -n "$out" | od -A x -t x1z -v
    
    000000 1b 5b 31 36 3b 31 52                             >.[16;1R<
    000007
    

    Note that the answer does not necessarily come on standard input: the answer comes from the terminal even if standard input is redirected.

    At the inquirer's request, here is a small C program which partially duplicates the functionality of the scriptlet above. Note that the program does not handle setting the terminal in raw mode and back to cooked mode; this must be handled outside the program, as indicated below.

    #include <errno.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main (void)
    
    {
      int ttyfd = open ("/dev/tty", O_RDWR);
      if (ttyfd < 0)
        {
          printf ("Cannot open /devv/tty: errno = %d, %s\r\n",
            errno, strerror (errno));
          exit (EXIT_FAILURE);
        }
    
      write (ttyfd, "\x1B[6n\n", 5);
    
      unsigned char answer[16];
      size_t answerlen = 0;
      while (answerlen < sizeof (answer) - 1 &&
             read (ttyfd, answer + answerlen, 1) == 1)
        if (answer [answerlen ++] == 'R') break;
      answer [answerlen] = '\0';
    
      printf ("Answerback = \"");
      for (size_t i = 0; i < answerlen; ++ i)
        if (answer [i] < ' ' || '~' < answer [i])
          printf ("\\x%02X", (unsigned char) answer [i]);
        else
          printf ("%c", answer [i]);
      printf ("\"\r\n");
    
      return EXIT_SUCCESS;
    }
    

    Assuming this little program is answerback.c:

    $ gcc -Wall -Wextra answerback.c -o answerback
    $ stty raw -echo; ./answerback; stty sane
    
    Answerback = "\x1B[24;1R"
    $ _
    
    0 讨论(0)
  • 2021-01-15 05:49
    #include <stdio.h>
    #include <termios.h>
    
    int
    main() {
     int x = 0, y = 0;
     get_pos(&y, &x);
     printf("x:%d, y:%d\n", x, y);
     return 0;
    }
    
    int
    get_pos(int *y, int *x) {
    
     char buf[30]={0};
     int ret, i, pow;
     char ch;
    
    *y = 0; *x = 0;
    
     struct termios term, restore;
    
     tcgetattr(0, &term);
     tcgetattr(0, &restore);
     term.c_lflag &= ~(ICANON|ECHO);
     tcsetattr(0, TCSANOW, &term);
    
     write(1, "\033[6n", 4);
    
     for( i = 0, ch = 0; ch != 'R'; i++ )
     {
        ret = read(0, &ch, 1);
        if ( !ret ) {
           tcsetattr(0, TCSANOW, &restore);
           fprintf(stderr, "getpos: error reading response!\n");
           return 1;
        }
        buf[i] = ch;
        printf("buf[%d]: \t%c \t%d\n", i, ch, ch);
     }
    
     if (i < 2) {
        tcsetattr(0, TCSANOW, &restore);
        printf("i < 2\n");
        return(1);
     }
    
     for( i -= 2, pow = 1; buf[i] != ';'; i--, pow *= 10)
         *x = *x + ( buf[i] - '0' ) * pow;
    
     for( i-- , pow = 1; buf[i] != '['; i--, pow *= 10)
         *y = *y + ( buf[i] - '0' ) * pow;
    
     tcsetattr(0, TCSANOW, &restore);
     return 0;
    }
    
    0 讨论(0)
提交回复
热议问题