如何使用getch() ncurses获取Ctrl、Shift或Alt键?

25
如何使用 ncurses 中的 getch() 函数获取 Ctrl、Shift 或 Alt?
我无法通过使用 ncurses 中的 getch() 函数来获取 Ctrl、Shift 或 Alt。我是否在手册中漏掉了什么?

5
Ctrl、Shift和Alt键不会产生输入,它们是用来修改其他输入的。 - Seth Carnegie
1
@SethCarnegie:我记得很多游戏都可以使用这些键进行单独的操作。我相信每个键都有一种独特的扫描码。 - Kerrek SB
2
@KerrekSB 他们不会生成标准输入的输入。你可以测试它们是否处于“关闭”状态(例如,Windows有GetAsyncKeyState),但这是与键盘一起工作,而不是像getch一样处理输入。 - Seth Carnegie
这个问题应该重新表述一下。现在它基本上是一个句子的近乎字面重复。 - Wolf
不清楚您是想要单独使用Alt键还是作为修改键(例如alt+c)。 - Al Ro
7个回答

29

有时候正确答案会被贬低,而那些“权威”放弃的答案会被提升,真是令人惊奇……通过一点创意,key_name实际上拥有解决这个问题的正确钥匙,只有一个注意事项——要与其他键一起按SHIFT/ALT/CTRL:

  • 首先,对于可打印字母之类的“普通键”,你可以很容易地检测到SHIFT,因为它会变成大写。

  • 对于特殊键,例如KEY_LEFT,当选择SHIFT时生成的代码实际上是KEY_SLEFT。右侧的KEY_RIGHT也是如此。不幸的是,对于似乎不受SHIFT影响的KEY_UP / KEY_DOWN,没有这样的运气。所以您可以通过从getch()返回的字符区分它们——KEY_S..意味着按下了Shift。

  • 对于ALT(至少没有被X或Aqua Windowmanager捕获的内容),keyname将键转换为M... something。

  • 对于CTRL,实际键名之前会有“^”。例如,键18的名称是^R。

因此,您现在可以通过以下简单的片段确定switch(getch)语句等的键代码:

ch = getch(); endwin(); printf("KEY NAME : %s - %d\n", keyname(ch),ch);

就这样了。在下定论之前要三思而后行,不要轻易说“不行”。也许有一种不太明显的方法。


4
这并不能帮助编写适用于多种键盘布局的代码。例如,Shift+2 可以根据布局生成不同的字符。 - Adam Spiers
1
我感谢这个答案。顺便说一下并确认显而易见的是,这种方法可能特定于C(因为问题被标记了)。我在Linux上尝试了Python,但Ctrl没有转换为“^”。 - creativecoding
3
这是因为Python绑定会进一步“加工”代码。 - Technologeeks
1
不是这样的。当我按下Shift-Enter来表示多行输入中的换行而不是对话框完成条件时,无论哪种情况下我都会得到'\r'。 - Mike
1
KEY_SRKEY_SF似乎分别来自SHIFT+UP和SHIFT+DOWN。 - schneiderfelipe

13

2
我没有尝试过这个,但是知道为什么它被踩了会很好。 - Jayant Bhawal
这是因为应该是大写的R @JayantBhawal - user129393192

7

(大致上复制自如何在Curses中获取Shift+X / Alt+X键的内容?的答案)

简而言之 - 你不能。修饰键只是修饰键,它们本身并不存在,它们只是修改你可能按下的其他(打印)键。

话虽如此,如果你感觉特别勇敢,你可以尝试我的libtermkey,它至少可以正确解析像Ctrl-箭头这样的内容。

最后,如果你感觉更加勇敢,你可以运行我编写的终端pangoterm,它具有通用的方法来编码任意修改过的Unicode键,因此它可以区分Ctrl-mEnterCtrl-Shift-aCtrl-a等...

然而,在这些范围之外,答案仍然是“你不能”。

5
同意 @leonerd 的看法,ncurses 仅提供那些作为其他按键的修饰符使用的按键(忽略某些人将 ASCII 的 escape 字符与 Alt 键混淆的情况)。一些特定设备可以告诉你如何获取这些信息(例如 Linux 控制台,在console_ioctl(4)中有记录),但这不是 ncurses 能为您解决的问题。
请参考 ncurses FAQ 如何使用 shift- 或 control- 修饰键? 获取详细回答。
简而言之,除了在某些众所周知使用了 shift 的特殊情况下,ncurses 不会告诉您是否使用了给定的修饰符,而是通过以下方式在其终端描述中提供信息:
  • 将实际功能键乘以 shift 和 control 修饰键的组合。
  • 使用基于 xterm 的 PC 风格功能键的名称(shift 是 2,alt 是 3,control 是 5 等)来提供信息。
这两种方法都适用,因为前者只使用不多于 60 个函数键的数组(对于 shift 和 control 组合已足够),而后者则只使用用户定义的名称。
所有这些修改键都会生成多个字节;在使用 ncurses 的 keypad() 函数时,应用程序将获得一个单一的数字。对于后一种情况,键码是在运行时确定的。
这主要适用于特殊键(功能键、编辑键和光标键)。对于常规键,则可以假设 keyname 提供了一些特殊行为,但阅读其说明并不是这样:
  • 它报告 ASCII 控制字符(您可以使用 iscntrl 宏进行处理)。
  • 关于 meta 的假设只有在您使用的终端中有用(大多数情况下仅适用于 xterm)。
  • 对于 shift 修饰符没有提供帮助。
至于终端……所有终端都可在内部获取修饰符信息,但终端通常没有一种方式将此信息传递给应用程序。xterm 可以使用 modifyOtherKeys 资源进行传递。
   modifyOtherKeys (class ModifyOtherKeys)
           Like modifyCursorKeys, tells xterm to construct an escape
           sequence for other keys (such as "2") when modified by
           Control-, Alt- or Meta-modifiers.  This feature does not apply
           to function keys and well-defined keys such as ESC or the
           control keys.  The default is "0":

           0    disables this feature.

           1    enables this feature for keys except for those with well-
                known behavior, e.g., Tab, Backarrow and some special
                control character cases, e.g., Control-Space to make a
                NUL.

           2    enables this feature for keys including the exceptions
                listed.

这对应于控制序列,可以在XTerm控制序列中看到:

CSI > Ps; Ps m
          Set or reset resource-values used by xterm to decide whether
          to construct escape sequences holding information about the
          modifiers pressed with a given key.  The first parameter iden-
          tifies the resource to set/reset.  The second parameter is the
          value to assign to the resource.  If the second parameter is
          omitted, the resource is reset to its initial value.
            Ps = 0  -> modifyKeyboard.
            Ps = 1  -> modifyCursorKeys.
            Ps = 2  -> modifyFunctionKeys.
            Ps = 4  -> modifyOtherKeys.

但在ncurses中没有理由使用它,因为它是特定于xterm的功能,会使getch变得不必要的复杂。


3
我使用双重getch()技巧。
#define ESC 27
int altpressed;

int sgetch()
{
  int t;
  
    t=getch();
    if ( t == ESC ) { // possible alt held down
     t=bgetch(10);
     if ( t == -1 ) // escape key pressed
      return ESC;
     altpressed=1;
    }

 return t;
}

现在是bgetch():

// getch with block/unblock
int bgetch(int delay)
{
  int t;  
    
   if ( delay != BLOCK )
    blockunblockgetch(delay);
  
   t=getch();
   
   blockunblockgetch();
   
 return t;
}

...和block/unblock/getch():

// block, unblock getch
int blockunblockgetch(int delay)
{
  wtimeout(win1, delay);
  
 return ( delay == -1 ) ? 0 : 1;
}

你拥有的只是一个getch(),如果检测到ESC,则阻止下一个getch()进行10毫秒。 如果流中还有另一个字符,则返回该字符(以int形式),并将altpressed(全局变量)设置为1。 然后,你可以利用alt +键来检查altpressed。SHIFT和CTRL更容易检测,它们被识别为NCURSES中的单个integer。

2

您可以调用key_name(c)将从getch()生成的键转换为显示ctrl修饰符状态的内容。

例如,如果按下ctrl-r,则此代码显示"^R":

while( true )
{
   char c = getch();
   if ( ERR == c )
      break;

   const char *name = key_name( c );

   move( 2, 2 );
   clear();
   printw( "You entered: %s             ", name );
   refresh();

}

2
getch 返回的是 chartype,而不是 char - yyny

-1

我知道这已经过时了,但对于仍在寻找的任何人... 你可以在Windows上做到这一点(至少在我的版本上)。 只需将getch解释为类似字节的整数,然后再转换为字符串即可。

str(getchvariablehere)

例如,我的键盘上Ctrl+A是1。
这有点繁琐,但您可以使用此方法检查任何组合。
我的措辞可能不太准确,但这种方法似乎适用于无法将字节显示为任意Unicode的光栅字体。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接