Ncurses reading numpad keys and escaping - c

I am trying to use ESC to escape from a program using getch(). I created a small program to demonstrate my problem.
#include <ncurses.h>
int main(void) {
int key = 0;
initscr();
noecho();
keypad(stdscr, TRUE);
do {
key = getch();
clear();
mvprintw(0, 0, "Key = %d\n", key);
refresh();
} while (key != 27);
clear();
refresh();
endwin();
return 0;
}
I am trying to allow a user to use either the arrow keys or keypad (whichever is more convenient)
the issue lies within the keypad (whether numlock is on or not). When I compile and run the program and try and use the numpad keys in this simple test it exits as soon as I touch a numpad key. If I remove the while (key != 27) (esc being 27) condition it reads the keys and displays their numbers. Why does it exit the loop when the numpad keys register as
ENTER 343
UP 120
DOWN 114
LEFT 116
RIGHT 118
Any help is much appreciated!

I found a fix in the source for Dungeon Crawl Stone Soup. It basically sets the keycodes for those.
{DCSS-dir}/source/libunix.cc (333)
define_key("\033Op", 1000);
define_key("\033Oq", 1001);
define_key("\033Or", 1002);
define_key("\033Os", 1003);
define_key("\033Ot", 1004);
define_key("\033Ou", 1005);
define_key("\033Ov", 1006);
define_key("\033Ow", 1007);
define_key("\033Ox", 1008);
define_key("\033Oy", 1009);
// non-arrow keypad keys (for macros)
define_key("\033OM", 1010); // Enter
define_key("\033OP", 1011); // NumLock
define_key("\033OQ", 1012); // /
define_key("\033OR", 1013); // *
define_key("\033OS", 1014); // -
define_key("\033Oj", 1015); // *
define_key("\033Ok", 1016); // +
define_key("\033Ol", 1017); // +
define_key("\033Om", 1018); // .
define_key("\033On", 1019); // .
define_key("\033Oo", 1020); // -
// variants. Ugly curses won't allow us to return the same code...
define_key("\033[1~", 1031); // Home
define_key("\033[4~", 1034); // End
define_key("\033[E", 1040); // center arrow

The XTERM terminal emulator sends an escape for certain numpad keys if Num Lock is off.
You can turn on Num Lock, use something other than the numpad, use something other than ESC to break your loop, or try to find a terminal emulator that doesn't do this. There is no way for your program to distinguish between ESC and certain numpad characters when Num Lock is off within the confines of your terminal emulator.

Related

ncurses "get_wch" function behavior

I try to understand how the get_wch function from ncurses works.
Here is a piece of code that I use to test it under TTY, terminator, and konsole:
#include <stdlib.h>
#include <curses.h>
#include <locale.h>
int main(void)
{
// initialize curses
initscr();
setlocale(LC_ALL, ""); // Just a check to see if something change with it
wint_t char_code;
int key_code = get_wch(&char_code);
char truc [20];
sprintf(truc, "%d / %d", key_code, char_code);
refresh();
getch();
endwin();
printf("%d\n", KEY_CODE_YES);
printf(truc);
printf("\n");
return 0;
}
When I hit a "classic" key like 'a' or '?', I get a char_code (UTF-8 code, I supposed). But when I hit a function key like F1 or F12, I get a char_code 27, and a key_code 0, except for F11 (key_code: KEY_CODE_YES, char_code: 410).
The documentation say that:
When get_wch, wget_wch, mvget_wch, and mvwget_wch functions successfully report the pressing of a function key, they return KEY_CODE_YES. When they successfully report a wide character, they return OK.
F1 to F12 are the so called "function keys" ? If I'm right, could you explain me why the function return 0 as a key_code when I hit a Fx key ?
You'll get KEY_CODE_YES only with keypad set to true, see keypad man page about that :
The keypad option enables the keypad of the user's terminal. If enabled (bf is TRUE),
the user can press a function key (such as an arrow key) and wgetch returns a single value
representing the function key, as in KEY_LEFT. If disabled (bf is FALSE), curses does not
treat function keys specially and the program has to interpret the escape sequences
itself. If the keypad in the terminal can be turned on (made to transmit) and off (made to
work locally), turning on this option causes the terminal keypad to be turned on when
wgetch is called. The default value for keypad is false.
And you're not getting keycode 0 but the success status OK from get_wch, see related manpage.
Without it, you'll have to get multiple char_code to get the key code like :
wint_t char_code;
wint_t char_code2 = 0, char_code3 = 0;
int key_code = get_wch(&char_code);
if (key_code == OK) key_code = get_wch(&char_code2);
if (key_code == OK) key_code = get_wch(&char_code3);
For function keys, you'll the escape code in char_code (0x27), the function code in char_code2 (0x4b) and the specific function key_code in char_code3.
EDIT As noticed by #dratenik, with xterm/VT220+ terminals, the F5-F12 function keys output four char_code, so you'll need to query another char_code when you get [ (0x5b) in char_code2 and 1 (0x31) in char_code3 to get the specific function keys key_code for theses function keys. You'll find the possible key codes in this useful link provided by #dratenik.

ncurses window close on trigger keyboard arrow [duplicate]

I've been trying to find a solution to this for quite a while; I cannot get my terminal window to recognize the keypad.
Here is my code:
while (1){
if (cmd != ERR) {
printw("Controls: LEFT/RIGHT and SPACEBAR (Q TO QUIT)");
if (cmd == ' '){
// Stuff
}
else if (cmd == KEY_LEFT){
// Stuff
}
else if (cmd == KEY_RIGHT){
// Stuff
}
else if (cmd == 'q'){
// Stuff
}
refresh();
}
cmd = getchar();
}
It recognizes that I am entering space (case 1) or "q" (case 4), but it will not recognize the built in curses macros. I'm using regular C, not C++ for this. Any suggestions?
Those constants are only returned by getch() if you enable the keypad.
The function signature is:
int keypad(WINDOW *win, bool bf)
keypad(win, true) enables the keypad and keypad(win, false) disables it. win is a pointer to the window where getch() is being called.
As mentioned by Alexandre Bell, keypad has to be enabled. Enable it for the default window or the window you are working with.
keypad(stdscr, TRUE); //Enables keypad for default window
If you are still having trouble getting it to work, make sure you are using an int datatype with getch(). The function prototype for getch() is:
int getch(void);
If you are using a char datatype for the return value of getch(), then it will not be large enough to fit the return value of special characters and you will get garbage values back.

How do I make characters in C using the ncurses.h library "blink"

Before I make one of these posts, I look around at around 5-10 other forums to see if my question has been answered.
There are a lot of websites that explain that my compiler doesn't have blinking enabled, and that I just need to download the package to enable it, or something to that affect
However, of all the ones that I have seen, none of them go into detail about where and how to acquire the package I need to allow blinking, or if they do then it isn't with my compiler.
So if someone could assist me, how do I enable blinking on Ubuntu with a function such as
attron(A_BLINK);
I'm aware that similarly phrased questions will get down-votes. I do not care, I just want to know how to fix my problem.
Any feedback would be really appreciated
-Edit
#include <string.h>
#include <ncurses.h>
int main(void)
{
char text[] = "Please Blink";
size_t len = strlen(text);
int i, row, col;
initscr();
getmaxyx(stdscr, row,col);
keypad(stdscr, TRUE);
noecho();
curs_set(0);
move((row / 2), (col / 2) - (len / 2));
attron(A_BLINK);
for(i = 0; i < len; ++i)
{
printw("%c", text[i]);
}
refresh();
getch();
attroff(A_BLINK);
endwin();
return 0;
}
-Ryan
ansi escape sequences
as an example of usage:
from the terminal type:
echo -e "\x1b[5;32;43m"
this results in a blinking green foreground over a yellow background
then type:
echo "test"
to display the word test as green letters over a yellow background
then, if the screen has not returned to normal operation, type:
echo -e "\1b[0m"
to reset the screen to normal
Note: the same ansi escape sequences can be output using the printf() function via:
printf( "%s\n", "\x1b[5;32;43m" );
and so on.
Blinking is a property of your terminal emulator. If your terminal does not support it, it won't happen.
FWIW, a plain xterm window does support blinking for me.
However, if you want the text to blink, you can manually make it happen by periodically overwriting the text with blanks. Something along the lines of:
int toggle = 0;
halfdelay(5);
do {
toggle = !toggle;
move((row / 2), (col / 2) - (len / 2));
printw("%*.*s", len, len, toggle ? text : "");
refresh();
} while (getch() == ERR);
nocbreak();
The halfdelay() call will cause getch() to return after 5/10ths of a second. If no key was hit within that time, ERR is returned. The nocbreak() call will disable halfdelay().

Using `sleep()` for a time delay [duplicate]

This question already has answers here:
Sleep for milliseconds
(20 answers)
Closed 8 years ago.
I an trying to delay program execution for 200ms and then test if a key was pressed during the delay. How do I do this?
I am tryint to create a simple computer game similar to flappy birds, using C. I want the user to have tiny bit of time (~200ms) to press a key for the bird to jump, or it will fall down, but I am having trouble with implementing the delay.
I've read on some forums [where?] that sleep(100) should give a 100ms delay, but when I do it, I get 100 seconds.
I also tried using sleep(1/5), but the function only takes integers.
Additionally, I need to be able to test if a key was pressed during the 200ms; I read somewhere[where?] that the kbhit function can be used for that, but I have no idea how to use it.
while(!dead) {
sleep(200); // what do I put here to get 200ms?
if (keyWasPressedDuringWait()){ //what do I put here?
notDeadAnimation():
}else{
dead=true;
deadAimation()
}
}
To perform the desired delay, #include <unistd.h> and use usleep(microseconds). (To sleep for 200ms, the call is usleep(200000)).
To test the keyboard strike, #include <conio.h> and use _kbhit() in your test (short for keyboard hit). _kbhit tests if there is a key in the key buffer, but does not get rid of it. You also need to use _getch to retrieve the key, removing it from the key buffer. I'd recommend defining a helper function here:
int clearKeyBuffer(){
int count = 0;
while(_kbhit()){
_getch();
count++;
}
return count;
}
This method will clear all keys currently in the key buffer, and return the number of keys cleared. You can then use this in your test, as if(clearKeyBuffer()) to test if a key has been presses since the last time you tested it.
As for your program flow, you have a lot of extra stuff there. You can get rid of most of it and still be functionally identical:
do {
notDeadAnimation();
usleep(200000);
} while(clearKeyBuffer());
deadAnimation();
However, this has the obvious issue that someone could just
Use usleep() instead of sleep(). The former works in micro seconds.
And use _kbhit()+getch() to discover if a key was pressed and which key was it:
while (!dead) {
usleep(200*1000); // 200 msec
if (_kbhit()) { // if key was pressed during sleep
int key = getch();
// you can check key value here
notDeadAnimation();
} else {
dead = true;
deadAnimation();
}
}

How to pause a loop in C/C++

I am trying to make a screen for a car game and make the screen wait for a key to go into the next screen, thing is that with this code it changes colors too fast. I've already tried delay() and sleep() which haven't worked properly. Also, after hitting a key, it closes and doesn't wait for me to enter a key. I just want the title to blink between white and red until a key is hit, and get to know why it exits after hitting a key.
Here is my code:
#include <dos.h>
#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
int main(void)
{
int gdriver = DETECT, gmode, errorcode;
initgraph(&gdriver, &gmode, "C|\\BORLANDC\\BGI");
outtextxy(250,280,"POINTER DRIVER 1.0");
outtextxy(250,290,"LCCM 10070249");
do
{
setcolor(WHITE);
outtextxy(250,380,"PRESS ANY KEY TO CONTINUE");
// delay(10); nothing works here :(
setcolor(RED);
outtextxy(250,380,"PRESS ANY KEY TO CONTINUE");
} while(!kbhit());
cleardevice();
outtextxy(250,290,"HELLO"); //here it draws mega fast and then exits
getch();
closegraph();
return 0;
}
Instead of using delay(10), maybe try using some sort of timer variable to do this. Try something like the following (a modification of your do-while loop):
unsigned flashTimer = 0;
unsigned flashInterval = 30; // Change this to vary flash speed
do
{
if ( flashTimer > flashInterval )
setcolor(RED);
else
setcolor(WHITE);
outtextxy(250,380,"PRESS ANY KEY TO CONTINUE");
++flashTimer;
if ( flashTimer > flashInterval * 2 )
flashTimer = 0;
// Remember to employ any required screen-sync routine here
} while(!kbhit());
kbhit() returns true if there's a character in the buffer, but doesn't remove the character before it returns. Once you reach the getch() line, it takes the first key that you pressed to break out of the while loop.
Possible solution: While it's a bit hacky, adding a getch() right after your while loop would probably fix it.
May I also suggest using ncurses instead of those Borland libraries?

Resources