I am trying to create a program that response to keyboard input. At the moment the menu below functions correctly but upon pressing enter I am running into issues. When I press enter nothing happens. I am just wondering why this is happening?
Many thanks!
#include <ncurses.h>
#define MENUMAX 6
void drawmenu(int item)
{
int c;
char mainmenu[] = "Menu";
char menu[MENUMAX] [10] = {
"1",
"2",
"3",
"4",
"5",
"6"
};
clear();
attron(A_BOLD | A_UNDERLINE);
addstr(mainmenu);
attroff(A_BOLD | A_UNDERLINE);
for( c = 0; c < MENUMAX; c++)
{
if( c == item)
attron(A_REVERSE);
attron(A_BOLD);
mvaddstr(3 + (c * 2), 20, menu[c]);
attroff(A_BOLD);
attroff(A_REVERSE);
}
refresh();
}
int main(int argc, char *argv[])
{
int key, menuitem;
menuitem = 0;
initscr();
drawmenu(menuitem);
keypad(stdscr, TRUE);
noecho();
do
{
raw();
nonl();
key = getch();
switch(key)
{
case KEY_DOWN:
menuitem++;
if(menuitem > MENUMAX - 1) menuitem = 0;
break;
case KEY_UP:
menuitem--;
if(menuitem < 0) menuitem = MENUMAX - 1;
break;
case KEY_ENTER:
mvaddstr(17, 25, "Test mesage!");
refresh();
break;
default:
break;
}
drawmenu(menuitem);
} while(key != '~');
echo();
endwin();
return 0;
}
The program has not provided for character-at-a-time (unbuffered) input, as described in the Initialization section of the ncurses manual page. Since it looks for special keys such as KEY_UP, that means it should use cbreak (rather than raw, which prevents ncurses from decoding special keys).
Related
I am pretty new to c++ and ncurses, so sorry if my question is weird or obvious.
I managed to make a simple tui program that will do various tasks, my problem is that I have no back functionality or something that would loop the program.
I want to be able to go back to the main menu after accessing one of the submenus and I want to make the program repeat after it's done so I won't have to keep opening it over and over, so do you have any solutions that I can apply?
Thank you in advance
This is my code:
int main(int argc, char ** argv)
{
int i=0;
string input;
initscr();
noecho();
cbreak();
curs_set(0);
int yMax, xMax;
getmaxyx(stdscr, yMax, xMax);
WINDOW * menuwin = newwin (0, xMax, 0, 0);
leaveok(menuwin, true);
refresh();
wrefresh(menuwin);
keypad(menuwin, true);
string choices[4] = {"Youtube","Music","Anime","Games"};
int choice;
int highlight = 0;
int option = 0;
while(1)
{
for (i=0;i < 4; i++)
{
if(i == highlight)
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, choices[i].c_str());
wattroff(menuwin, A_REVERSE);
}
choice = wgetch(menuwin);
switch(choice)
{
case KEY_UP:
highlight--;
if(highlight == -1)
highlight = 3;
break;
case KEY_DOWN:
highlight++;
if(highlight == 4)
highlight = 0;
break;
default:
break;
}
if(choice == 10)
{
break;
}
}
werase(menuwin);
switch(highlight)
{
case 0:
{
menu2();
break;
}
case 1:
{
menu3();
break;
}
case 2:
{
menu4();
break;
}
case 3:
{
menu2();
}
default:
break;
}
endwin();
return 0;
}
I tried to write a program which hooks keyboard messages to pronounce the name of each key whenever it is pressed in Ubuntu (KDE); without interfering with normal action of keyboard in programs (just announcing the key name).
This is my program:
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
void SendPressKeyEvent(Display *display, XKeyEvent xkey)
{
Window current_focus_window;
int current_focus_revert;
XGetInputFocus(display, ¤t_focus_window, ¤t_focus_revert);
xkey.type = KeyPress;
xkey.display = display;
xkey.window = current_focus_window;
xkey.root = DefaultRootWindow(display);
xkey.subwindow = None;
xkey.time = 1000 * time(0);
xkey.x = 0;
xkey.y = 0;
xkey.x_root = 0;
xkey.y_root = 0;
xkey.same_screen = True;
XSendEvent(display, InputFocus, True, KeyPressMask, (XEvent *)(&xkey));
}
void SendReleaseKeyEvent(Display *display, XKeyEvent xkey)
{
Window current_focus_window;
int current_focus_revert;
XGetInputFocus(display, ¤t_focus_window, ¤t_focus_revert);
xkey.type = KeyRelease;
xkey.display = display;
xkey.window = current_focus_window;
xkey.root = DefaultRootWindow(display);
xkey.subwindow = None;
xkey.time = 1000 * time(0);
xkey.x = 0;
xkey.y = 0;
xkey.x_root = 0;
xkey.y_root = 0;
xkey.same_screen = True;
XSendEvent(display, InputFocus, True, KeyReleaseMask, (XEvent *)(&xkey));
}
void *TaskCode(void* arg)
{
switch(*(int*)arg)
{
case 38:
system("espeak -v en " "\"a\"");
}
return 0;
}
int main()
{
Display *display = XOpenDisplay(0);
if(display == 0)
exit(1);
XGrabKeyboard(display, DefaultRootWindow(display), True, GrabModeAsync, GrabModeAsync, CurrentTime);
XEvent event;
while(true)
{
XNextEvent(display, &event);
if(event.type == Expose)
{
}
if(event.type == KeyPress)
{
SendPressKeyEvent(display,event.xkey);
if(event.xkey.keycode == 38)
{
pthread_t thread;
int thread_arg = event.xkey.keycode;
pthread_create(&thread,0, TaskCode, (void*) &thread_arg);
}
}
if(event.type == KeyRelease)
SendReleaseKeyEvent(display,event.xkey);
}
XCloseDisplay(display);
}
This program is just for the key a which can be extended to other keys.
But when this program is running, some programs (e.g. Chromium) do not show the blinker (cursor) in their edit boxes. Also all KDE hotkeys become disabled.
How can this be fixed?
Here's my quick and dirty example
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <ctype.h>
int main ()
{
Display* d = XOpenDisplay(NULL);
Window root = DefaultRootWindow(d);
Window curFocus;
char buf[17];
KeySym ks;
XComposeStatus comp;
int len;
int revert;
XGetInputFocus (d, &curFocus, &revert);
XSelectInput(d, curFocus, KeyPressMask|KeyReleaseMask|FocusChangeMask);
while (1)
{
XEvent ev;
XNextEvent(d, &ev);
switch (ev.type)
{
case FocusOut:
printf ("Focus changed!\n");
printf ("Old focus is %d\n", (int)curFocus);
if (curFocus != root)
XSelectInput(d, curFocus, 0);
XGetInputFocus (d, &curFocus, &revert);
printf ("New focus is %d\n", (int)curFocus);
if (curFocus == PointerRoot)
curFocus = root;
XSelectInput(d, curFocus, KeyPressMask|KeyReleaseMask|FocusChangeMask);
break;
case KeyPress:
printf ("Got key!\n");
len = XLookupString(&ev.xkey, buf, 16, &ks, &comp);
if (len > 0 && isprint(buf[0]))
{
buf[len]=0;
printf("String is: %s\n", buf);
}
else
{
printf ("Key is: %d\n", (int)ks);
}
}
}
}
It's not reliable but most of the time it works. (It is showing keys I'm typing into this box right now). You may investigate why it does fail sometimes ;) Also it cannot show hotkeys in principle. Hotkeys are grabbed keys, and only one client can get a grabbed key. Absolutely nothing can be done here, short of loading a special X11 extension designed for this purpose (e.g. XEvIE).
Thanks to n.m.'s answer and parsa's comment, this is my final code:
#include <X11/Xlib.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
void* TaskCode(void* parg)
{
int keycode = *((int*)parg);
cout<< "\n\n" << keycode << "\n\n";
if(keycode == XKeysymToKeycode(XOpenDisplay(0),'a'))
system("espeak -v en " "\"a\"");
delete (int*)parg;
return 0;
}
void Action(int keycode)
{
pthread_t thread;
pthread_attr_t attrs;
pthread_attr_init(&attrs);
pthread_attr_setdetachstate(&attrs,PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attrs, 1000);
int* pthread_arg = new int;
*pthread_arg = keycode;
pthread_create(&thread,&attrs, TaskCode, (void*) pthread_arg);
}
int MyX11ErrorHandler(Display *, XErrorEvent *error_event)
{
cout << "\n\n" "An X11-Functions error occured. Probably the focused window was closed.""\n"
"This error will be ignored." "\n";
cout<< "error_code: " << (unsigned)error_event -> error_code << "\n";
cout<< "minor_code: " << (unsigned)error_event -> minor_code << "\n";
cout<< "request_code: " << (unsigned)error_event -> request_code << "\n";
cout<< "resourceid: " << error_event -> resourceid << "\n";
cout<< "serial; " << error_event -> serial << "\n";
cout<< "type: " << error_event -> type << "\n\n";
return 0;
}
int main()
{
Display* display = XOpenDisplay(0);
Window root = DefaultRootWindow(display);
Window current_focus_window;
int revert;
XSetErrorHandler(MyX11ErrorHandler);
XGetInputFocus(display, ¤t_focus_window, &revert);
XSelectInput(display,current_focus_window,KeyPressMask | KeyReleaseMask | FocusChangeMask);
while(true)
{
XEvent event;
XNextEvent(display, &event);
switch (event.type)
{
case FocusOut:
if(current_focus_window != root)
XSelectInput(display, current_focus_window, 0);
XGetInputFocus(display, ¤t_focus_window, &revert);
if(current_focus_window == PointerRoot)
current_focus_window = root;
XSelectInput(display, current_focus_window, KeyPressMask|KeyReleaseMask|FocusChangeMask);
break;
case KeyPress:
Action(event.xkey.keycode);
break;
}
}
}
Add these to a Qt Creator's project .pro file:
LIBS += -lX11
LIBS += -lpthread
LIBS += -lXtst
Any improvement suggestions is appreciated.
To archive I also add my final code with grabbing:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
void* TaskCode(void* parg)
{
int keycode = *((int*)parg);
cout<< "\n\n" << keycode << "\n\n";
system("espeak -v en " "\"a\"");
delete (int*)parg;
return 0;
}
void SendKeyEvent(Display *display, XEvent event)
{
Window current_focus_window;
XKeyEvent& xkey = event.xkey;
int current_focus_revert;
XGetInputFocus(display, ¤t_focus_window, ¤t_focus_revert);
xkey.state = Mod2Mask;
XSendEvent(display, InputFocus, True, xkey.type, (XEvent *)(&event));
}
int GrabKey(Display* display, Window grab_window, int keycode)
{
unsigned int modifiers = Mod2Mask; // numlock on
//Window grab_window = DefaultRootWindow(display);
Bool owner_events = True;
int pointer_mode = GrabModeAsync;
int keyboard_mode = GrabModeAsync;
XGrabKey(display, keycode, modifiers, grab_window, owner_events, pointer_mode, keyboard_mode);
return keycode;
}
void UngrabKey(Display* display, Window grab_window, int keycode)
{
unsigned int modifiers = Mod2Mask; // numlock on
// Window grab_window = DefaultRootWindow(display);
XUngrabKey(display,keycode,modifiers,grab_window);
}
void Action(int keycode)
{
pthread_t thread;
int* pthread_arg = new int;
*pthread_arg = keycode;
pthread_create(&thread,0, TaskCode, (void*) pthread_arg);
}
int main()
{
Display* display = XOpenDisplay(0);
Window root = DefaultRootWindow(display);
XEvent event;
int keycode = XKeysymToKeycode(display,'a');
GrabKey(display,root,keycode);
XSelectInput(display, root, KeyPressMask | KeyReleaseMask);
while(true)
{
XNextEvent(display, &event);
switch(event.type)
{
case KeyPress:
Action(event.xkey.keycode);
case KeyRelease:
SendKeyEvent(display,event);
default:
break;
}
}
XCloseDisplay(display);
}
Everything is good except that, unlike the code in question, it ignores language layout. Pressing a types a whatever regradless of language layout!
As an alternative to listening to X events, it's also possible to listen to Linux input events directly: https://stackoverflow.com/a/27693340/21501
This has the benefit that it's possible to modify the event stream in-flight, and block, edit or generate input events.
The proper way to listen to all events is using the X Record Extension Library, part of libXtst, apparently installed on pretty much every X system. It is documented here, but as the docs are patchy, you will need to browse previous implementations of this. Here is a nice working demo, and here is a more capable and complete implementation.
A simplified version of the first example is included below.
#include <stdio.h>
#include <X11/XKBlib.h>
#include <X11/extensions/record.h>
void key_pressed_cb(XPointer arg, XRecordInterceptData *d);
int scan(int verbose) {
XRecordRange* rr;
XRecordClientSpec rcs;
XRecordContext rc;
Display *dpy = XOpenDisplay(NULL);
rr = XRecordAllocRange();
rr->device_events.first = KeyPress;
rr->device_events.last = ButtonReleaseMask;
rcs = XRecordAllClients;
rc = XRecordCreateContext (dpy, 0, &rcs, 1, &rr, 1);
XFree (rr);
XRecordEnableContext(dpy, rc, key_pressed_cb, NULL);
}
void key_pressed_cb(XPointer arg, XRecordInterceptData *d) {
if (d->category != XRecordFromServer)
return;
int key = ((unsigned char*) d->data)[1];
int type = ((unsigned char*) d->data)[0] & 0x7F;
int repeat = d->data[2] & 1;
if(!repeat) {
switch (type) {
case KeyPress:
printf("key press %d\n", key);
break;
case KeyRelease:
printf("key release %d\n", key);
break;
case ButtonPress:
printf("button press %d\n", key);
break;
case ButtonRelease:
printf("button release %d\n", key);
break;
default:
break;
}
}
XRecordFreeData (d);
}
int main() {
scan(True);
return 0;
}
gcc -o x1 x1.c -lX11 -lXtst
I'm new to ncurses and we have a project to create a game of our choice.
The idea of my game is to have a spaceship and have enemies attack from the top.
I only started the code and I already ran into a problem, when i use the space bar to shoot a bullet, the bullet will travel, however i am unable to move my ship at the same time the bullet is moving.
#include<ncurses.h>
typedef struct
{/*define a structure for player information*/
int row;
int col;
}playerinfo;
int changeRow(int x,int m, int c)
{
if(x+m==22 || x+m==0 )
{
beep();
return x;
}
return x+m;
}
int changeColumn(int y,int n, int r)
{
if(y+n==72 || y+n==0 )
{
beep();
return y;
}
return y+n;
}
int main(){
initscr();
start_color();
assume_default_colors(COLOR_GREEN, COLOR_BLACK);
noecho();
cbreak();
curs_set(0); /* turn cursor display off */
timeout(0);
keypad(stdscr,TRUE); /* allow keypad keys to be used */
playerinfo playership;
playership.row= 10;
playership.col= 15;
char player[] =" X ";
char player2[]=" |o| ";
char player3[]=" xX| |Xx ";
char player4[]=" X | | X ";
char player5[]=" X__-|-__X ";
char bullet = '.';
int key = 0;
int i=0;
bool moving= false;
mvprintw(playership.row,playership.col,player);
mvprintw(playership.row+1,playership.col,player2);
mvprintw(playership.row+2,playership.col,player3);
mvprintw(playership.row+3,playership.col,player4);
mvprintw(playership.row+4,playership.col,player5);
timeout(0);
while(key!='q'){
usleep(17000);
mvprintw(playership.row,playership.col," ");
mvprintw(playership.row+5,playership.col," ");
key = getch ();
switch(key){
case KEY_UP: playership.row=changeRow(playership.row,-1,playership.col); /* move up */
break;
case KEY_DOWN: playership.row=changeRow(playership.row,+1,playership.col); /* move down */
break;
case KEY_LEFT:playership.col=changeColumn(playership.col,-1,playership.row); /* move left */
break;
case KEY_RIGHT:playership.col=changeColumn(playership.col,+1,playership.row); /* move right */
break;
case ' ': moving=true; break;
default: break; /* do nothing if other keys */
}
mvprintw(playership.row,playership.col,player);
mvprintw(playership.row+1,playership.col,player2);
mvprintw(playership.row+2,playership.col,player3);
mvprintw(playership.row+3,playership.col,player4);
mvprintw(playership.row+4,playership.col,player5);
if (moving==true){
for( i=0; i <24; i++){
refresh; mvprintw(playership.row-i-2,playership.col+5,"%c", bullet); mvprintw(playership.row,playership.col,player);
refresh();usleep(12000); mvprintw(playership.row-i-1,playership.col+5," ");} moving=false; }
refresh();
}
echo(); /* turn echo back on */
endwin(); /* End curses mode */
return 0;
}
That is because you nested the "firing" code inside your game loop, so all input stops while you do that animation. You need to change your logic to a kind of state machine:
switch(key)
{
// ...
case ' ':
if( !bullet_active )
{
bullet_active = true;
bullet_pos = 0;
}
break;
}
if( bullet_active ) {
// TODO: Draw bullet at bullet_pos
if( ++bullet_pos == 24 ) bullet_active = false;
}
I am trying to get familiar with ncurses and I have a problem - the constants such as KEY_LEFT seem to be wrong.
For example, I'm trying to catch keyboard arrows. Should be as simple as
while (ch = getch()){
if (ch == KEY_LEFT)
foo();
}
But that does not work. I had the ch written out and it says this - left arrow is 68, right 67, up 65, down 66.
That wouldn't be such a problem, but when trying to catch mouse events, it goes bad. Leftclicking the terminal gives me values from 33 to 742, least when clicking upper left corner, most when clicking bottom right corner. What the hell?
This is my whole main, just in case
int main(void){
initscr();
start_color();
init_pair(1, COLOR_YELLOW, COLOR_WHITE);
init_pair(2, COLOR_RED, COLOR_RED);
cbreak();
//printw("Hai!\n");
noecho();
int width;
int height;
getmaxyx(stdscr, height, width);
int posx = 30;
int posy = 30;
const char* str = " ";
const char* hint = "ch = %d";
curs_set(0);
mousemask(BUTTON1_CLICKED, NULL);
unsigned int ch = 0;
while (ch = getch()) {
attron(COLOR_PAIR(1));
mvprintw(0, 0, hint, ch);
mvdelch(posy,posx);
mvdelch(posy, posx);
mvdelch(posy, posx);
switch (ch) {
case 68:
if (posx > 0) posx--;
//mvprintw(1,0,"LEFT");
break;
case 67:
if (posx < width) posx++;
break;
case 65:
if (posy > 0) posy--;
break;
case 66:
if (posy < height) posy++;
break;
case KEY_MOUSE:
MEVENT event;
if (getmouse(&event)==OK){
posx = event.x;
posy = event.y;
}
}
attron(COLOR_PAIR(2));
mvprintw(posy, posx, str);
refresh();
}
endwin();
return 0;
}
As (almost) always a look in the manual helps. Taking a look at man getch
Function Keys
The following function keys, defined in curses.h, might be returned by getch
if keypad has been enabled. Note that not all of these are necessarily supportā
ed on any particular terminal.
it seems you missed
keypad (stdscr, TRUE);
in your program. At least it doesn't occur in the snippet you posted.
Is there any function that can wait for input until a certain time is reached? I'm making kind of Snake game.
My platform is Windows.
For terminal based games you should take a look at ncurses.
int ch;
nodelay(stdscr, TRUE);
for (;;) {
if ((ch = getch()) == ERR) {
/* user hasn't responded
...
*/
}
else {
/* user has pressed a key ch
...
*/
}
}
Edit:
See also Is ncurses available for windows?
I found a solution using kbhit() function of conio.h as follows :-
int waitSecond =10; /// number of second to wait for user input.
while(1)
{
if(kbhit())
{
char c=getch();
break;
}
sleep(1000); sleep for 1 sec ;
--waitSecond;
if(waitSecond==0) // wait complete.
break;
}
Try with bioskey(), this is an example for that:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <bios.h>
#include <ctype.h>
#define F1_Key 0x3b00
#define F2_Key 0x3c00
int handle_keyevents(){
int key = bioskey(0);
if (isalnum(key & 0xFF)){
printf("'%c' key pressed\n", key);
return 0;
}
switch(key){
case F1_Key:
printf("F1 Key Pressed");
break;
case F2_Key:
printf("F2 Key Pressed");
break;
default:
printf("%#02x\n", key);
break;
}
printf("\n");
return 0;
}
void main(){
int key;
printf("Press F10 key to Quit\n");
while(1){
key = bioskey(1);
if(key > 0){
if(handle_keyevents() < 0)
break;
}
}
}
Based on #birubisht answer I made a function which is a bit cleaner and uses NON-deprecated versions of kbhit() and getch() - ISO C++'s _kbhit() and _getch().
Function takes: number of seconds to wait for user input
Function returns: _ when user does not put any char, otherwise it returns the inputed char.
/**
* Gets: number of seconds to wait for user input
* Returns: '_' if there was no input, otherwise returns the char inputed
**/
char waitForCharInput( int seconds ){
char c = '_'; //default return
while( seconds != 0 ) {
if( _kbhit() ) { //if there is a key in keyboard buffer
c = _getch(); //get the char
break; //we got char! No need to wait anymore...
}
Sleep(1000); //one second sleep
--seconds; //countdown a second
}
return c;
}