Detecting shift + keyup/down in the terminal
From the context of the question, shift+up
and shift+down
refer to shift-cursor-up
and shift-cursor-down
(rather than, for instance, shift-page-up
and shift-page-down
), because the question asked about selecting multiple lines.
This is actually more than one question:
- first, how to make ncurses recognize
shift+up
, etc., and - whether the ncurses menu library does this, and
- how to get a menu doing that, if not.
First, ncurses provides predefined (per X/Open Curses) definitions for a portable (more/less) set of special keys. Referring to terminfo(5), you might notice:
key_sf kind kF scroll-forward key
key_sleft kLFT #4 shifted left-arrow
key
key_sr kri kR scroll-backward key
key_sright kRIT %i shifted right-arrow
key
but there is nothing labeled "shifted up-arrow key". Only with some hindsight can one related kind
and kri
to kLFT
and kRIT
. Before adding support for modified special-keys to xterm in 1999, there was little prior art:
Patch #94 - 1999/3/27 - XFree86 3.9Pf
add parameters to function keys to indicate if shift, control or alt are set. The codes are based on a description of a DEC VT510 with a PC keyboard, from Jeffrey Altman .
Later, that scheme was amended to reduce application confusion. Other developers who copied the feature from xterm did not follow suit by amending their programs:
Patch #167 - 2002/8/24 - XFree86 4.2.0
addmodifyCursorKeys
resource to control how the shift- and similar modifiers are used to make a cursor escape sequence. The default makes a modified escape sequence always start with CSI and puts the modifier as the second parameter, to avoid confusing applications that would interpret the first parameter as a repeat count. The original behavior can be obtained by setting the resource to 0 (newsgroup discussion with Stephen J Turnbull, Jeffrey Altman).
Meanwhile, in ncurses, it seemed useful to incorporate these into terminal descriptions (because ncurses uses the terminal database than, say, tables such as those that tmux uses). These rely upon a feature for user-defined capabilities introduced by ncurses 5.0. In the terminal database, the feature is mentioned for xterm in 2004:
# 2004-07-17
# * add xterm-pc-fkeys -TD
which (besides consolidating earlier work as early as 2001) attempted to deal with the variations of modifier encodings that might be used with different combinations of xterm resources — as well as the look-alikes which differed in varying degrees from the xterm encoding scheme.
The convention used for the user-defined capabilities started with adding kDN
and kUP
to the list (your shift+down
and shift+up
), and a number corresponding to the xterm modifier codes. Much later, it became apparent that kind
and kri
could have the same meaning. But as a result, you might find terminal descriptions with kDN
and kUP
(which only ncurses reads — other applications and libraries which read the terminal database directly having neglected this for some 16 years).
The extended capabilities are summarized in the terminal database.
Now (beginning the second part), ncurses does provide a menu library. This is (with a few extensions such as supporting multibyte characters) a reimplementation of the SVr4 menu library. Refer to the manual page for menu_driver(3x)
for that, as well as the programs in ncurses-examples which demonstrate the library. The short answer is that the menu library does not predefine the behavior you are asking about, but that an application can use the library to do what is asked. Start by looking to see how to use this detail:
REQ_TOGGLE_ITEM
Select/deselect an item.
The larger issue is that raised by using user-defined capabilities in this application. Most of the applications using the form- and menu-libraries rely on making case-statements with the predefined symbols for special keys. For shift+up
and shift+down
you would not have that (unless the coincidence of kind
and kri
was noted and applied). For user-defined capabilities, there are no predefined key-codes (such as KEY_DOWN
in curses.h
). You have instead runtime-defined values determined by the key_defined
function. So instead of a simple case-statement (there is no KEY_SDOWN
in curses.h
), some indirection is needed.
Terminals transmit characters, not keys. Most characters are printable, which doesn't leave much room to encode function keys and keychords. Function keys are encoded as escape sequences, i.e. sequences of bytes starting with the escape character (byte value 27, which can be written out as \e
in many programming languages). For more details, see How do keyboard input and text output work?
There's no universal standard on what escape sequence each key or keychord sends. The curses library provides an abstraction layer that decodes function keys. It uses the termcap (old-style) or terminfo (modern) library under the hood; you can use termcap/terminfo directly if you don't want to link with curses.
Alternatively, you can hard-code the usual control sequences for Up and Down. While there is no standard, almost every terminal sends either \eOA
or \e[A
for Up and either \eOB
or \e[B
for Down, and I've never seen a terminal that sent these sequences for a different key.
The Shift keychords are another matter: the escape sequences they send are more diverse, and many terminals don't distinguish e.g. Up from Shift+Up. You can't rely on these being distinct unless you only support a restricted set of configurations. There are emerging standards but they're still not supported by many popular terminal emulators.
Terminals don't send key up events. (Your use case doesn't require them, though, according to your description.)
Unless you can afford to require a specific terminal emulator (e.g. recent-ish xterm), don't rely on the user being able to type Shift+Up. Support an alternate method, such as a “start multiple selection” key followed by plain Up/Down.