I've been struggling to get any input from Ncurses working. The keyboard input works fine if I use stdscr rather than my custom window. However, if I use the stdscr, I get absolutely no output to the terminal.
Here is my compile argument:
gcc -o dungeon dungeon.c -lncursesw
Code in question:
setlocale(LC_ALL, "");
int key;
window = newwin(XLEN, YLEN, 0, 0);
keypad(window, TRUE);
initscr();
clear();
noecho();
cbreak();
refresh();
while (player.alive && !playerWin(monsters)){
key = wgetch(window);
if (key == 'y' || key == '7'){
player.alive = 0;
}
render(monsters);
refresh();
for(i = 0; i < numMonsters; i++){
moveMonsters(&monsters[i]);
}
monsterPlayerCollision(monsters);
int dead = monsterMonsterCollision(monsters);
if(dead){
monsters[dead].alive = 0;
}
dijkstraNon();
dijkstraTunnel();
clear();
usleep(3);
wrefresh(window);
}
Please let me know if there's anything I can do to clarify.
Related
I'm having a hard time understanding the problem with a window in ncurses. I created a layout of several windows and want to have a function that will border a window. Look like this:
int main() {
use_env(TRUE);
initscr();
start_color();
cbreak();
noecho();
curs_set(0);
WINDOW *win_corner = newwin(WCLINES, WCCOLS, WCX, WCY);
choose_lvl(win_corner);
...
}
int choose_lvl(WINDOW *win) {
box(win, 0, 0);
wrefresh(win);
refresh();
int c, curr_option = 0;
init_pair(1, COLOR_CYAN, COLOR_BLACK);
attron(COLOR_PAIR(1));
mvwprintw(win, 1, 1, "Choose your level");
...
}
It seems like box() doesnt have any effect at all - what could it be? My current theory is to suppose something wrong with the coordinates of the window.
refresh() refreshes the updates on stdscr. newwin() creates an independant window. A refresh on stdscr does not update the alternate window. The same without refresh() in choose_lvl() function would work fine as you would force update only on the alternate window.
You can also create a sub-window of stdscr with subwin() to make refresh() update the whole hierarchy:
#include <unistd.h>
#include <curses.h>
#define WCLINES 10
#define WCCOLS 80
#define WCX 4
#define WCY 4
int choose_lvl(WINDOW *win) {
box(win, 0, 0);
//wrefresh(win);
//refresh();
int c, curr_option = 0;
init_pair(1, COLOR_CYAN, COLOR_BLACK);
attron(COLOR_PAIR(1));
mvwprintw(win, 1, 1, "Choose your level");
refresh();
return 0;
}
int main() {
use_env(TRUE);
initscr();
start_color();
cbreak();
noecho();
curs_set(0);
//WINDOW *win_corner = newwin(WCLINES, WCCOLS, WCX, WCY);
WINDOW *win_corner = subwin(stdscr, WCLINES, WCCOLS, WCX, WCY);
choose_lvl(win_corner);
// quick&dirty pause to suspend program execution
// right after the windows display
pause();
return 0;
}
Execution:
$ gcc win.c -lncurses
$ ./a.out
The result is:
I want to put a string or bytes to the X11 selection so that when I switch to other applications, I can directly do a ctrl+p paste.
I try to follow the documentation of the X11 clipboard mechanism. If I understand correctly, I need to use XSetSelectionOwner to obtain the XA_CLIPBOARD selection and then use XChangeProperty to put my data to the clipboard.
Here is a simple snippet, but unfortunately it does not work:
// main.c
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/Atoms.h>
int main() {
// try to write `hello` to the clipboard
const char *in = "hello\0";
const int n = 5;
Display* d = XOpenDisplay(0);
Window w = XCreateSimpleWindow(d, DefaultRootWindow(d), 0, 0, 1, 1, 0, 0, 0);
Atom XA_CLIPBOARD = XInternAtom(d, "CLIPBOARD", True);
XSetSelectionOwner(d, XA_CLIPBOARD, w, CurrentTime);
XEvent event;
XNextEvent(d, &event);
if (event.type != SelectionRequest) {
XCloseDisplay(d);
return 0;
}
if (event.xselectionrequest.selection != XA_CLIPBOARD) {
XCloseDisplay(d);
return 0;
}
XSelectionRequestEvent* req = &event.xselectionrequest;
XChangeProperty(d, req->requestor, req->property, XA_STRING, 8, PropModeReplace, (unsigned char *)in, n);
XEvent re = {0};
re.xselection.type = SelectionNotify;
re.xselection.display = req->display;
re.xselection.requestor = req->requestor;
re.xselection.selection = req->selection;
re.xselection.property = req->property;
re.xselection.target = req->target;
XSendEvent(d, req->requestor, 0, 0, &re); // event is sent, but data is not in my clipboard
XFlush(d);
XCloseDisplay(d);
return 0;
}
Compile: clang -o main main.c -lX11 -lXmu
What did I do wrong, and how to fix it?
XNextEvent retrieves any event that occured (input, pointer, configuration, property changes, ...).
So it is very unlikely that the first event, will be your desired event.
You have to iterate over the event queue to check if a SelectionRequest event occured.
Therefore you have to call XNextEvent in a loop and check everytime the event.type for the desired event.
Edit:
If you retrieve a ClientMessage event and the client data equals the Atom WM_DELETE, there was a request by the user to close the window (pressing the X). Other than that, you can quit whenever you want.
XEvent evt;
while (i_want_to_receive_and_process_events) {
XNextEvent(dpy, &evt);
switch (evt.type) {
case SelectionRequest:
if (evt.xselectionrequest.selection == XA_CLIPBOARD) {
// what you were looking for, do your thing
// i_want_to_receive_and_process_events = false (?)
}
break;
case ClientMessage:
if (evt.xclient.data.l[0] == WM_DELETE) {
i_want_to_receive_and_process_events = false;
}
break;
}
}
I'm using the following simple ncurses code to create a menu:
#include <menu.h>
#include <stdlib.h>
ITEM **it;
MENU *me;
WINDOW *win;
void quit(void)
{
int i;
unpost_menu(me);
free_menu(me);
for(i=0; i<=4; i++)
{
free_item(it[i]);
}
free(it);
delwin(win);
endwin();
}
int main(void)
{
int ch;
initscr();
atexit(quit);
clear();
noecho();
curs_set(0);
cbreak();
nl();
keypad(stdscr, TRUE);
start_color();
init_pair(1, COLOR_WHITE, COLOR_BLUE);
init_pair(2, COLOR_BLUE, COLOR_YELLOW);
bkgd(COLOR_PAIR(1));
it = (ITEM **)calloc(5, sizeof(ITEM *));
it[0] = new_item("M1", "Menueeintrag 1");
it[1] = new_item("M2", "Menueeintrag 2");
it[2] = new_item("M3", "Menueeintrag 3");
it[3] = new_item("Ende", "Programm beenden");
it[4] = 0;
me = new_menu(it);
win = newwin(8, 30, 5, 5);
set_menu_win (me, win);
set_menu_sub (me, derwin(win, 4, 28, 3, 2));
box(win, 0, 0);
mvwaddstr(win, 1, 2, "***** Testmenü *****");
set_menu_fore(me, COLOR_PAIR(1)|A_REVERSE);
set_menu_back(me, COLOR_PAIR(1));
wbkgd(win, COLOR_PAIR(2));
post_menu(me);
mvaddstr(14, 3, "Programm mittels Menü oder F1-Funktionstaste beenden");
refresh();
wrefresh(win);
while((ch=getch()) != KEY_F(1))
{
switch(ch)
{
case KEY_DOWN:
menu_driver(me, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(me, REQ_UP_ITEM);
break;
case 0xA: /* Return- bzw. Enter-Taste -> ASCII-Code */
if(item_index(current_item(me)) == 3)
exit(0);
}
wrefresh(win);
}
return (0);
}
But at the positions where I put text there is always a weird misalignment. You can see it when looking at the border of the menu as well as at the right border of the terminal on the same height as the help text.
Screenshot of the resulting program
That umlaut on "Testmenü" tells the story: it is probably encoded as UTF-8, which uses two bytes:
252: 252 0374 0xfc text "\374" utf8 \303\274
If the locale settings are (for example) en_US, then that tells ncurses that the terminal doesn't use UTF-8, and it will assume that the string is actually ISO-8859-1. In that case, it counts both bytes of that character as separate cells on the screen.
Meanwhile, the terminal may be using UTF-8 encoding (and display the character as shown in the screenshot). But if the locale settings do not correspond to the terminal, you'll see odd stuff like that.
I'm making a program with ncurses that splits the screen into two windows. The top screen can accept input, and by pressing '#' it will move all the text down the the bottom window and wipe the top window. In my code I'm trying to use copywin() to replace the bottom window, but it will not paste the wording in the second window. This is what I have...
#include <ncurses.h>
int main(int argc, char *argv[])
{
// Declare variables for windows and sizes
WINDOW *top_win, *bottom_win;
int maxx, maxy, halfy, flag = 0, ch;
// Start curses
initscr();
noecho();
refresh();
// get the max x's and y's
getmaxyx(stdscr, maxy, maxx);
halfy = maxy >> 1;
// Start color
start_color();
init_pair(1, COLOR_BLACK, COLOR_WHITE);
init_pair(2, COLOR_WHITE, COLOR_CYAN);
init_pair(3, COLOR_RED, COLOR_WHITE);
// Make windows
top_win = newwin(halfy, maxx, 0, 0);
wbkgd(top_win, COLOR_PAIR(1));
wrefresh(top_win);
bottom_win = newwin(halfy, maxx, halfy, 0);
wbkgd(bottom_win, COLOR_PAIR(2));
wrefresh(bottom_win);
// Allow functions keys
keypad(top_win, TRUE);
keypad(bottom_win, TRUE);
// while loop to get input
while((ch = getch()) != '`')
{
if(ch == '#')
{
if(flag == 1)
{
flag = 0;
}
else
{
flag = 1;
}
}
else if(ch == '#')
{
//waddstr(bottom_win, "testing");
copywin(top_win, bottom_win, 0, 0, halfy, 0, halfy, maxx, TRUE);
//overwrite(top_win, bottom_win);
//werase(top_win);
}
else if(flag != 1)
{
waddch(top_win, ch | COLOR_PAIR(1));
}
else if(flag == 1)
{
waddch(top_win, ch | COLOR_PAIR(3));
}
wrefresh(top_win);
wrefresh(bottom_win);
}
// end curses
delwin(top_win);
delwin(bottom_win);
endwin();
return 0;
}
I know I can print to the window using the '#' character because of my commented out, testing statement. I've also just tried using overwrite(), but that didn't work either. Am I simply getting the arguments mixed up, or is it something else? Any Ideas? Thank you in advance!
copywin checks the given rows/columns, and decides that your destination rectangle doesn't lie completely within the destination window. Here's a quick fix for your program:
--- foo.c.orig 2017-02-13 16:13:12.000000000 -0500
+++ foo.c 2017-02-13 16:30:18.037987489 -0500
## -51,7 +51,7 ##
else if(ch == '#')
{
//waddstr(bottom_win, "testing");
- copywin(top_win, bottom_win, 0, 0, halfy, 0, halfy, maxx, TRUE);
+ copywin(top_win, bottom_win, 0, 0, 0, 0, halfy - 1, maxx - 1, TRUE);
//overwrite(top_win, bottom_win);
//werase(top_win);
}
## -73,4 +73,3 ##
endwin();
return 0;
}
Rows and columns are numbered from zero through the last row/column (which is one less than the window size), so I subtracted one from the dmaxrow and dmaxcol parameters. The fifth parameter dminrow was past the bottom of the window.
ncurses checks the parameters. Regarding compatibility and portability, running the same program with Solaris curses (changing "ncurses.h" to "curses.h") dumps core.
The manual page could be improved, but it's clear enough regarding colors:
only text where the two windows overlap is copied
I don't have a good explanation for why it works, but as long as xoff and yoff are at least 1 in the code below, the data from the upper window is copied to the lower window OK (and cleared from the upper window). The colour isn't copied. If either offset is 0, the data is not copied. The string testing is added at the top-left of the lower window — it can be omitted and the copied material is still OK.
#include <ncurses.h>
int main(void)
{
// Declare variables for windows and sizes
WINDOW *top_win, *bottom_win;
int maxx, maxy, halfy, flag = 0, ch;
// Start curses
initscr();
noecho();
refresh();
// get the max x's and y's
getmaxyx(stdscr, maxy, maxx);
halfy = maxy >> 1;
// Start color
start_color();
init_pair(1, COLOR_BLACK, COLOR_WHITE);
init_pair(2, COLOR_WHITE, COLOR_CYAN);
init_pair(3, COLOR_RED, COLOR_WHITE);
// Make windows
top_win = newwin(halfy, maxx, 0, 0);
wbkgd(top_win, COLOR_PAIR(1));
wrefresh(top_win);
bottom_win = newwin(halfy, maxx, halfy, 0);
wbkgd(bottom_win, COLOR_PAIR(2));
wrefresh(bottom_win);
// Allow functions keys
// keypad(top_win, TRUE);
// keypad(bottom_win, TRUE);
// while loop to get input
int xoff = 1;
int yoff = 1;
while ((ch = getch()) != '`')
{
if (ch == '#')
{
if (flag == 1)
{
flag = 0;
}
else
{
flag = 1;
}
}
else if (ch == '#')
{
waddstr(bottom_win, "testing");
// copywin(top_win, bottom_win, 0, 0, halfy, 0, halfy, maxx, TRUE);
copywin(top_win, bottom_win, 0, 0, yoff, xoff, halfy-yoff, maxx-xoff, TRUE);
// overwrite(top_win, bottom_win);
werase(top_win);
}
else if (flag != 1)
{
waddch(top_win, ch | COLOR_PAIR(1));
}
else if (flag == 1)
{
waddch(top_win, ch | COLOR_PAIR(3));
}
wrefresh(top_win);
wrefresh(bottom_win);
}
// end curses
delwin(top_win);
delwin(bottom_win);
endwin();
return 0;
}
Testing on Mac running macOS Sierra 10.12.3 with GCC 6.3.0, using the local -lncurses library.
I am trying to get into the ncurses library. I am dissapointed that the Gnome Terminal can print red via ANSI escape characters but not the predefined red from ncurses.
#include <curses.h>
void init_colors() {
int a;
for(a = 1; a < COLORS; a++) {
init_pair(a, a, COLOR_BLACK);
}
}
void print_colors() {
int a;
for(a = 1; a < COLORS; a++) {
attron(COLOR_PAIR(a));
mvprintw(a, 0, "color");
attroff(COLOR_PAIR(a));
}
}
int main() {
initscr();
cbreak();
noecho();
curs_set(0);
if(has_colors())
start_color();
init_colors();
print_colors();
getch();
endwin();
return 0;
}
This should print the word "color" in any default ncurses color, but the second line (init_pair should initialize the second COLOR_PAIR as red) is not printed at all. It seems that Gnome Terminal simply skip this line. How can I fix this?
This is a copy of another stackoverflow answer
#include <curses.h>
int main(void) {
initscr();
start_color();
init_pair(1, COLOR_BLACK, COLOR_RED);
init_pair(2, COLOR_BLACK, COLOR_GREEN);
attron(COLOR_PAIR(1));
printw("This should be printed in black with a red background!\n");
attron(COLOR_PAIR(2));
printw("And this in a green background!\n");
refresh();
getch();
endwin();
}
Notes
you need to call start_color() after initscr() to use color.
you have to use the COLOR_PAIR macro
to pass a color pair allocated with init_pair to attron et al.
you can't use the color pair 0.
you only have to call refresh() once,
and only if you want your output to be seen at that point,
and you're not calling an input function like getch().