用stdin识别箭头键

时间:2010-11-09 02:46:13

标签: c input terminal ocaml stdin

是否可以采用跨平台方式处理C或OCaml程序中的退格键和箭头键?

实际上可以理解OCaml解决方案,但许多标准的unix函数直接包装到相应的API调用中,因此移植C解决方案应该没有问题。

我要实现的是捕获箭头键以覆盖它在shell中的行为(通过重新排列最后一行或类似这样的操作)。我认为这件事落在实际程序之前并且它不是由代码本身处理的,所以我不知道它是否可能。

该程序是在Linux,OS X和Windows(在cygwin上)编译的,所以我想在所有平台上进行编译..

4 个答案:

答案 0 :(得分:8)

我最近做了一些非常相似的事情(虽然我的代码只是Linux)。您必须将stdin设置为非规范模式才能读取箭头键。这应该适用于OS X和Linux,并且可能适用于Cygwin,尽管我不能肯定地说。

open Unix
let terminfo = tcgetattr stdin in
  let newterminfo = {terminfo with c_icanon = false; c_vmin = 0; c_vtime = 0} in
    at_exit (fun _ -> tcsetattr stdin TCSAFLUSH terminfo); (* reset stdin when you quit*)
    tcsetattr stdin TCSAFLUSH newterminfo;

当规范模式关闭时,您无需等待换行符以便从标准输入读取。 c_vmin表示返回之前要读取的最小字符数(您可能希望一次能够读取一个字符),c_vtime是最大读取等待时间(以0.1s为单位)。

您可能还想将c_echo设置为false,以便按下箭头键打印到终端(但之后您必须手动打印其他所有内容。

大多数终端使用ANSI escape sequences表示按箭头键。如果您运行没有参数的cat并开始按箭头键,您可以看到使用的转义序列。它们通常是

up - "\033[A"
down - "\033[B"
left - "\033[D"
right - "\033[C"

其中'\ 033'是esc

的ascii值

答案 1 :(得分:5)

使用ncurses提取箭头键功能的序列,然后在读取stdin时查找它们。使用像libedit或readline这样的东西可能更容易,并且让它处理密钥处理。

答案 2 :(得分:2)

支持键盘输入超出可打印字符行的标准方法是通过ncurses库,其中Ocaml binding。另一种常见的可能性是readline库(最常用于Bash)。

如果您所做的只是逐行读取输入,但希望您的用户拥有一个不错的行编辑器,则无需在您的程序中包含任何支持。相反,只需告诉您的用户使用包装程序,例如rlwrap(基于readline)或ledit。这些包装器确实提供了行版本和历史记录,这两个功能是您列出的要求。我建议您只在需要更高级的东西时才在程序中构建输入处理,例如当用户按下 Tab 时程序特定的完成。

答案 3 :(得分:1)

Backspace是一个ASCII字符,将像任何其他字符一样放在stdin中。字符转义序列'\b'是退格符。

对于光标键,这些键与任何控制字符都没有关联,因此不要在stdin流中生成数据。低级访问必然是特定于平台的,尽管有跨平台库可以消除平台差异。我相信ncurses适用于您提到的所有平台。