【问题标题】:How to get cursor position in C using ANSI code如何使用 ANSI 代码在 C 中获取光标位置
【发布时间】:2018-06-16 03:25:36
【问题描述】:

我正在尝试从一个小 c 程序中获取光标位置,所以在谷歌搜索后我发现了这个 ANSI 代码\x1b[6n。它应该返回光标的 x 和 y 位置(如果我没记错的话) 所以 printf("\x1b[6n"); 正在给我输出:;1R 我无法理解 x 和 y 位置的输出。

编辑: 平台是 Linux (xterm)

【问题讨论】:

标签: c ansi


【解决方案1】:
#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;
}

【讨论】:

  • 一个问题,在这种情况下,if(i&lt;2) 在您的代码中为真
  • 我不记得了。
  • 我的意思是你在写这行之前想到了什么原因或逻辑if(i&lt;2)
  • @KoenduBuf:为什么让你感到惊讶?这是预期的行为。 管道不是终端;管道不是终端,它们没有生与熟模式的概念。当你运行··· | ./compiled时,./compiled的文件描述符0是一个管道;对tcgetattr()tcsetattr() 的调用将失败。
  • @KoenduBuf:如果stdout是终端,可以使用文件描述符1。如果stderr是终端,可以使用文件描述符2。(使用isatty()查找。)如果程序有一个控制终端,即/dev/tty
【解决方案2】:

一些终端上,例如DEC VT102和更高版本的VT,以及在许多终端仿真器上,特别是XTerm和它的许多仿制品,发送Esc [ 6 n 将使终端响应 Esc [ row ; column R,其中rowcolumn是位置的十进制表示文本光标。

所以你的终端模拟器没有回复;1R;它正在正确回复,但 readline 例程正在吃 Esc [ 和直到 ; 的十进制数字(并闪烁屏幕或按铃,取决于配置)。

这是一个很好的 Bash 命令来说明:

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

运行此命令:

$ 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

请注意,答案不一定来自标准输入:即使标准输入被重定向,答案来自终端

应询问者的要求,这里有一个小 C 程序,它部分复制了上述 scriptlet 的功能。请注意,该程序不处理将终端设置为原始模式并返回到熟模式;这必须在程序之外处理,如下所示。

#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;
}

假设这个小程序是answerback.c:

$ gcc -Wall -Wextra answerback.c -o answerback
$ stty raw -echo; ./answerback; stty sane

Answerback = "\x1B[24;1R"
$ _

【讨论】:

  • 我不熟悉 bash,所以你的插图对我没有帮助,你能在 C 中给出同样的结果吗?
  • @Aux,添加了 C 小程序。
  • 我怀疑您必须执行该循环才能从终端读取。最好从终端读取。阅读(tty,answer,4096);
  • @AlexP 我在上面的答案中添加了一个关于问题的评论,任何帮助将不胜感激:D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-04
相关资源
最近更新 更多