Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

proper way of catching control+key in ncurses

Tags:

What is the proper way of catching a control+key in ncurses? current im doing it defining control like this:

#define ctl(x) ((x) & 0x1f)

it works ok, but the problem is that i cannot catch C-j and ENTER at the same time, and this is because:

   j = 106 = 1101010
0x1f = 31 = 0011111
1101010 & 0011111 = 0001010 = 10 = ENTER key..

So.. how shall I catch it? Thanks!

-- Edit: If i try the code below, I am not able to catch the enter key correctly, not even in the numeric keyboard. Enter gets catched as ctrl-j.

#include <stdio.h>
#include <ncurses.h>
#define ctrl(x) ((x) & 0x1f)

int main(void) {
    initscr();
    int c = getch();
    nonl();
    switch (c) {
        case KEY_ENTER:
            printw("key: %c", c);
            break;
        case ctrl('j'):
            printw("key: ctrl j");
            break;
    }
    getch();
    endwin();
    return;
}

New code:

#include <stdio.h>
#include <ncurses.h>
#define ctrl(x)           ((x) & 0x1f)

int main(void) {
    initscr();
    int l = -1;
    int c = getch();
    cbreak();
    noecho();
    nonl();
    keypad(stdscr, TRUE);
    switch (c) {
        case KEY_ENTER:
            printw("key: %c", c);
            break;
        case ctrl('j'):
            printw("key: ctrl j");
            break;
    }
    printw("\nnow press a key to end");
    getch();
    endwin();
    return;
}
like image 325
Andres Martinelli Avatar asked May 11 '17 18:05

Andres Martinelli


1 Answers

Try nonl:

The nl and nonl routines control whether the underlying display device translates the return key into newline on input, and whether it translates newline into return and line-feed on output (in either case, the call addch('\n') does the equivalent of return and line feed on the virtual screen). Initially, these translations do occur. If you disable them using nonl, curses will be able to make better use of the line-feed capability, resulting in faster cursor motion. Also, curses will then be able to detect the return key.

Further reading: the Notes section of the getch manual page:

Generally, KEY_ENTER denotes the character(s) sent by the Enter key on the numeric keypad:

  • the terminal description lists the most useful keys,
  • the Enter key on the regular keyboard is already handled by the standard ASCII characters for carriage-return and line-feed,
  • depending on whether nl or nonl was called, pressing "Enter" on the regular keyboard may return either a carriage-return or line-feed, and finally

  • "Enter or send" is the standard description for this key.

That addresses the question about newline/carriage-return translation. A followup comment is a reminder to point out that the manual page gives basic advice in the Initialization section:

To get character-at-a-time input without echoing (most interactive, screen oriented programs want this), the following sequence should be used:

     initscr(); cbreak(); noecho();

and that OP's sample program did not use cbreak (or raw). The manual page for cbreak says

Normally, the tty driver buffers typed characters until a newline or carriage return is typed. The cbreak routine disables line buffering and erase/kill character-processing (interrupt and flow control characters are unaffected), making characters typed by the user immediately available to the program. The nocbreak routine returns the terminal to normal (cooked) mode.

Initially the terminal may or may not be in cbreak mode, as the mode is inherited; therefore, a program should call cbreak or nocbreak explicitly. Most interactive programs using curses set the cbreak mode. Note that cbreak overrides raw. (See curs_getch(3x) for a discussion of how these routines interact with echo and noecho.)

Also, in curs_getch you may read

If keypad is TRUE, and a function key is pressed, the token for that function key is returned instead of the raw characters:

  • The predefined function keys are listed in <curses.h> as macros with values outside the range of 8-bit characters. Their names begin with KEY_.

That is, curses will only return KEY_ENTER if the program calls keypad:

keypad(stdscr, TRUE);

For the sake of discussion, here is an example fixing some of the problems with your sample program as of May 17:

#include <stdio.h>
#include <ncurses.h>
#define ctrl(x)           ((x) & 0x1f)

int
main(void)
{
    int c;
    initscr();
    keypad(stdscr, TRUE);
    cbreak();
    noecho();
    nonl();
    c = getch();
    switch (c) {
    case KEY_ENTER:
        printw("\nkey_enter: %d", c);
        break;
    case ctrl('j'):
        printw("\nkey: ctrl j");
        break;
    default:
        printw("\nkeyname: %d = %s\n", c, keyname(c));
        break;
    }
    printw("\nnow press a key to end");
    getch();
    endwin();
    return 0;
}

That is, you have to call keypad before getch, and the value returned for KEY_ENTER is not a character (it cannot be printed with %c).

Running on the Linux console with the usual terminal description, you will see only carriage return for the numeric keypad Enter, because that description does not use application mode. Linux console does support application mode, and a corresponding description could be written. As a quick check (there are differences...) you could set TERM=vt100 to see the KEY_ENTER.

like image 194
Thomas Dickey Avatar answered Sep 22 '22 10:09

Thomas Dickey