I am trying to create a snake game in C using the ncurses library. I would like my program to detect user input at a constant tick speed. If there is no input after a certain amount of time I would like my program to continue along (ie. update game).
This is what I wrote to test out the halfdelay(i) function:
#include <ncurses.h>
int main(void)
{
initscr();
halfdelay(5);
int user_input;
do
{
user_input = getch();
if (user_input != -1)
{
clear();
printw("Key pressed: %d\n", user_input);
}
else
{
//printf("Timeout.\n");
printw("Timeout.\n");
}
} while (user_input != ESC);
endwin();
return 0;
}
EDIT #1:
I would like to see
Timeout.
Timeout.
Timeout.
Code used:
#include <ncurses.h>
int main(void)
{
initscr();
halfdelay(5);
int in;
do
{
timeout(1);
in = getch();
if (in != -1)
{
clear();
printw("Key pressed: %d\n", in);
}
else
{
//printf("Timeout.\n");
printw("Timeout.\n");
}
refresh();
} while (in != 27);
endwin();
return 0;
}
Some history first.
Traditionally, the ESC key value has been used as the prefix for escape sequences, which denote special sequences of characters that can be interpreted as a non-graphic character (e.g., an arrow key, a function key, etc.)
Due to (n)curses focus on portability (and telecommunication), out of the box it supports this notion of escape sequences, and as such pressing the ESC key can have some side effects. Notably, when keypad is enabled, there is an inbuilt delay as the program waits to decide if the user simply pressed ESC or if it needs to wait for some more information to complete the escape sequence. This timing can be adjusted via the ESCDELAY environment variable, or the set_escdelay function.
All this is important as you work forward, as given its a game you may want to enable the functionality of the keypad eventually, which will create some extra steps when using the ESC key.
And because of all this, there is no ESC or KEY_ESC macro for for the escape key. Instead, its raw code is 27 (or the octal 033).
Your use of the halfdelay function seems perfectly fine to me, just know that the argument is in tenths of a second, so 5 is half a second. Tenths of a second may not achieve the desired effect in a game, so consider using the timeout function instead, which allows for higher precision.
A simple, working example:
#include <ncurses.h>
int main(void) {
initscr();
halfdelay(5);
int user_input;
while ((user_input = getch()) != 27) {
clear();
if (user_input == ERR)
printw("Timeout.");
else
printw("Key pressed: %d\n", user_input);
refresh();
}
endwin();
}
Your updated program "works", it just doesn't clear the screen properly. Note that you probably don't want to mix calls to printw and printf as it creates a strange mess of the screen.
Also, you should use halfdelay or timeout, but not both. Remember that timeout takes its argument in milliseconds, which is 1/1000 of a second, and it sets the blocking delay for a window (timeout for stdscr, wtimeout for specific windows). It's not a "sleep" style function.
Use one delay function at a time, move clear to outside the if statement, and use printw. This is functionally the same program to the one posted above, just with a do ... while loop.
#include <ncurses.h>
int main(void) {
initscr();
/*
noecho();
scrollok(stdscr, TRUE);
*/
timeout(500);
int in;
do {
in = getch();
clear();
if (in != ERR)
printw("Key pressed: %d\n", in);
else
printw("Timeout.\n");
refresh();
} while (in != 27);
endwin();
}
If you are expecting the display of this program to be something along the lines of
Timeout.
Timeout.
Key pressed: 65
Timeout.
...
like a more traditional terminal, then you simply want to remove clear all together, and un-comment noecho and scrollok.
Related
I want to make a program that runs forever and takes a user input, something like
while(1)
{
if(currentkeybeingpressed != NULL)
{
print(the current character); /* So that the program just waits until a key is
pressed and outputs the same letter the moment it is touched*/
}
}
I want to do this on a KISS controller, which does not have the conio.h file ready to import and therefore I cannot use the getch function. Is there another way to find out what key is being pressed at the moment?
#include<stdio.h>
int main() {
while(1){
putchar(getchar());
}
}
Hope this helps.
In a program that uses the curses or ncurses library, the getch() function can recognize either an ordinary character (like 'x') or the escape sequence sent by the arrow and function keys. For example, typing the up arrow key might send the sequence (Escape, '[', 'A'), but getch() will return the int value KEY_UP. It even uses timing information to distinguish between an Escape character sent by itself and an Escape character that's part of a sequence.
To use getch() successfully, you first have to call initscr(), which clears the screen so that curses can control what's displayed.
Is there a convenient way to recognize special keys without taking over the screen? Ideally I'd like to call getch() without first calling initscr(), and have it return the same value it would have returned if I had called initscr(), but experiment indicates that doesn't work.
Here's a non-working example of what I'm trying to do. The program prompts the user to enter Up, Down, Escape, Left, Right, and conforms that the correct keys were entered.
Running the program with -c invokes initscr() and allows it to work correctly. Running it without -c calls getch() without first calling initscr(); all the getch() calls fail without waiting for input, returning ERR (-1).
// gcc arrows.c -o arrows -lncurses
#include <term.h>
#include <stdio.h>
#include <curses.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int use_curses = 0;
if (argc == 2 && strcmp(argv[1], "-c") == 0) {
use_curses = 1;
}
else if (argc != 1) {
fprintf(stderr, "Usage: %s [-c]\n", argv[0]);
exit(EXIT_FAILURE);
}
WINDOW *win;
if (use_curses) {
win = initscr();
cbreak();
noecho();
nonl();
intrflush(stdscr, FALSE);
keypad(stdscr, TRUE);
mvaddstr(0, 0, "Press UP, DOWN, Escape, LEFT, RIGHT");
}
else {
puts("Press UP, DOWN, Escape, LEFT, RIGHT");
}
int keys[5];
for (int i = 0; i < 5; i ++) {
keys[i] = getch();
}
int ok;
if (keys[0] == KEY_UP &&
keys[1] == KEY_DOWN &&
keys[2] == '\x1b' &&
keys[3] == KEY_LEFT &&
keys[4] == KEY_RIGHT)
{
ok = 1;
if (use_curses) {
mvaddstr(2, 0, "OK");
}
else {
puts("OK");
}
}
else {
ok = 0;
char message[100];
sprintf(message,
"Incorrect input: (%d, %d, %d, %d, %d)",
keys[0], keys[1], keys[2], keys[3], keys[4]);
if (use_curses) {
mvaddstr(2, 0, message);
}
else {
puts(message);
}
}
if (use_curses) {
mvaddstr(4, 0, "Press any key to quit ");
(void)getch();
endwin();
}
exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
}
The output without the -c option (on Ubuntu 17.04 x86_64) is:
Press UP, DOWN, Escape, LEFT, RIGHT
Incorrect input: (-1, -1, -1, -1, -1)
UPDATE: A solution using something other than curses would be fine. It would have to use terminfo, directly or indirectly, to know what input sequences to look for. (It seems to me that recognizing special key inputs should be straightforward and not intimately tied to taking over the entire screen.)
The filter function, called before initscr, tells curses to use a single line (rather than the whole screen). There is an example of its use (named "filter") in ncurses-examples, which consists of a command-prompt.
getch updates at least part of the screen, e.g., when it echoes the input. That update can affect either just a line (using filter), or the whole screen. If you're clever, you can turn off echo, and (since filter suppresses the screen erasure), pretend that it updates nothing at all. For example, you could redirect the output to /dev/null by initializing curses with newterm.
Echoing input is only part of the story: curses does initialize the screen, and the associated refresh done by getch would make that hard to avoid in a portable way. There's an ncurses-specific option in the sample program which works around unwanted switching between normal/alternate screens, which could occur during screen-initialization.
Here's a screenshot of "filter", demonstrating that it does not take over the whole screen. The inverse video is due to ncurses initializing colors (white-on-black), but as the help-message indicates, that's configurable. Without making a movie, it's hard to demonstrate that it handles special keys, but reading the source code should be enough.
I'm trying to make a game where objects move when certain keys have been pressed. But I want to implement it so that stuff is moving and loops are running all the time. So far the keyboard input functions such as scanf and getchar have all been waiting for me to press a key until they allow the program to continue. How do i listen for key presses without stalling the program (without using threads)?
There is a way of implementing this using a loop.
The following program will do something else in a loop, and in every pass it will check if a key has been pressed. The pressed key will then be stored to a variable c.
#include <stdio.h>
#include <conio.h>
int main(void) {
char c = '\0';
while (1) {
if (kbhit()) {
c = getch();
}
/* If a key has been pressed */
if (c != '\0') {
/* Do something */
c = '\0'; // and put it back to \0
}
/* Do something else */
}
return 0;
}
Note: This solution works only on Windows and other operating systems where the header file "conio.h" can be included
I'm trying to get a non-blocking I/O on a Windows terminal application (windows only, sorry!).
What if I want to have a short input time in wich the user can press a button, but if he doesn't the input stops and the program continues?
For example:
A timer that counts from 1 to whatever that stops when the user presses a certain key:
I should have a while loop, but if I do a getch or a getchar function it will stop the program, right?
I know I could use kbhit(); , but for the "program" I'm trying to make I need to know the input, not just IF THERE IS input!
Are there any simple functions that would allow me to read like the last key in the keyboard buffer?
From the documentation for _kbhit():
The _kbhit function checks the console for a recent keystroke. If the function returns a nonzero value, a keystroke is waiting in the buffer. The program can then call _getch or _getche to get the keystroke.
So, in your loop:
while (true) {
// ...
if (_kbhit()) {
char c = _getch();
// act on character c in whatever way you want
}
}
So, you can still use _getch(), but limit its use to only after _kbhit() says there is something waiting. That way it won't block.
Here is how to make a non blocking call to stdin in windows by using the right API :
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
void ErrorExit(LPSTR);
void KeyEventProc(KEY_EVENT_RECORD ker);
// Global variables are here for example, avoid that.
DWORD fdwSaveOldMode;
HANDLE hStdin;
void printToCoordinates(int x, int y, char* text)
{
printf("\033[%d;%dH%s", y, x, text);
}
int main()
{
printf("\033[H\033[J");
int i = 0;
char* s = "*";
DWORD fdwMode, cNumRead;
INPUT_RECORD irInBuf[128];
DWORD bufferSize = 0;
hStdin = GetStdHandle(STD_INPUT_HANDLE);
// Just a check to ensure everything is fine at this state
if (hStdin==INVALID_HANDLE_VALUE){
printf("Invalid handle value.\n");
exit(EXIT_FAILURE);
}
// Just a check to ensure everything is fine at this state
if (! GetConsoleMode(hStdin, &fdwSaveOldMode) )
ErrorExit("GetConsoleMode");
// Those constants are documented on Microsoft doc
// ENABLE_PROCESSED_INPUT allows you to use CTRL+C
// (so it's not catched by ReadConsoleInput here)
fdwMode = ENABLE_WINDOW_INPUT | ENABLE_PROCESSED_INPUT;
if (! SetConsoleMode(hStdin, fdwMode) )
ErrorExit("SetConsoleMode");
while (i < 60) {
// The goal of this program is to print a line of stars
printToCoordinates(i, 5, s);
i++;
GetNumberOfConsoleInputEvents(hStdin, &bufferSize);
// ReadConsoleInput block if the buffer is empty
if (bufferSize > 0) {
if (! ReadConsoleInput(
hStdin, // input buffer handle
irInBuf, // buffer to read into
128, // size of read buffer
&cNumRead) ) // number of records read
ErrorExit("ReadConsoleInput");
// This code is not rock solid, you should iterate over
// irInBuf to get what you want, the last event may not contain what you expect
// Once again you'll find an event constant list on Microsoft documentation
if (irInBuf[cNumRead-1].EventType == KEY_EVENT) {
KeyEventProc(irInBuf[cNumRead-1].Event.KeyEvent);
Sleep(2000);
}
}
Sleep(100);
}
// Setting the console back to normal
SetConsoleMode(hStdin, fdwSaveOldMode);
CloseHandle(hStdin);
printf("\nFIN\n");
return 0;
}
void ErrorExit (LPSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
// Restore input mode on exit.
SetConsoleMode(hStdin, fdwSaveOldMode);
ExitProcess(0);
}
void KeyEventProc(KEY_EVENT_RECORD ker)
{
printf("Key event: \"%c\" ", ker.uChar.AsciiChar);
if(ker.bKeyDown)
printf("key pressed\n");
else printf("key released\n");
}
Please notice this work in brand new Terminal application but not in CMD (due to termcaps used in the code) but it will compile and you can run it anyway.
So, here is my problem. I am using ncurses, and when I press the up or down button, it says up arrow, and so I expect to move on in to my while loop, and take further input. The problem is, the variable input somehow in this process becomes NULL and it exists. Why is that? What I want is for it print up or down arrow, and the program to proceed normally.
int ch;
char input[100];
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
ch = getch();
if(ch== KEY_UP)
printf("\nUp Arrow"); fflush(stdout);
if(ch== KEY_DOWN)
printf("\ndown Arrow");fflush(stdout);
endwin();
Here is an SSCCE (Short, Self-Contained, Complete Example) based on your code:
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
int main(void)
{
int ch;
char input[100];
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
ch = getch();
endwin();
if (ch== KEY_UP)
printf("\nUp Arrow\n");
if (ch== KEY_DOWN)
printf("\ndown Arrow\n");
fflush(stdout);
while(1)
{
if (fgets(input, sizeof (input), stdin) == NULL)
{
printf("Early exit\n");
exit(0);
}
printf("Read: %s", input);
fflush(stdout);
}
printf("Late exit\n");
return 0;
}
When I run this, it correctly detects if I use the up or down arrow keys, reporting it after I've called endwin() rather than before. The fflush() calls would not normally be necessary, but it seems that the standard output is fully buffered rather than line buffered after you finish with curses, which is a little surprising. (I'm testing on Mac OS X 10.8.2.)
The other trick I've used is diagnostic prints so I know what's happening where. I called the program curses, and when I ran it, the screen cleared; then I typed an up arrow (in this case), then sssddd and return, then Control-D to indicate end of input (EOF), and the rest of the output was:
$ ./curses
Up Arrow
sssddd
Read: sssddd
Early exit
$
It is all behaving as I'd expect except for the buffering on standard output. There isn't a way to get to the Late exit print statement in this program.
As stated input is undefined. Also, fgets is used for file input streams, not for reading the std input stream. (Use gets instead)