如何使用 ANSI 代码在 C 中获取光标位置
How to get cursor position in C using ANSI code
我试图从一个小的 c 程序中获取光标位置,所以在谷歌搜索后我找到了这个 ANSI 代码 \x1b[6n
。它应该 return 光标的 x 和 y 位置(如果我没记错的话)
所以
printf("\x1b[6n");
给我输出:;1R
我无法理解 x 和 y 位置方面的输出。
编辑:
平台是 Linux (xterm)
在一些终端上,例如DEC VT102 and later VTs, and on many terminal emulators, notably XTerm及其许多仿制品,发送Esc [ 6 n 将使终端响应 Esc [ 行 ; 列 R, 其中行和列是文本光标位置的十进制表示。
所以你的终端模拟器不是回复;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] = '[=12=]';
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"
$ _
#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, "3[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;
}
我试图从一个小的 c 程序中获取光标位置,所以在谷歌搜索后我找到了这个 ANSI 代码 \x1b[6n
。它应该 return 光标的 x 和 y 位置(如果我没记错的话)
所以
printf("\x1b[6n");
给我输出:;1R
我无法理解 x 和 y 位置方面的输出。
编辑: 平台是 Linux (xterm)
在一些终端上,例如DEC VT102 and later VTs, and on many terminal emulators, notably XTerm及其许多仿制品,发送Esc [ 6 n 将使终端响应 Esc [ 行 ; 列 R, 其中行和列是文本光标位置的十进制表示。
所以你的终端模拟器不是回复;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] = '[=12=]';
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"
$ _
#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, "3[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;
}