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.
Related
I have a program that draws on a x11 window and I'd like to quit it as soon as I press any key.
With the code I have, I cannot stop the program with a keypress until the for loop has finished drawing on the window (moving a red oval from left to right). I'd like to quit the for loop as soon as a key is pressed regardless of the state of the drawing (finished or not) even if the for loop hasn't finished executing its part.
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <unistd.h>
Display *dis;
Window win;
unsigned long GetColor(char *color_name)
{
Colormap cmap;
XColor near_color, true_color;
cmap = DefaultColormap(dis, 0);
XAllocNamedColor(dis, cmap, color_name, &near_color, &true_color);
return(near_color.pixel);
}
int main(int argc, char **argv)
{
if ((dis = XOpenDisplay(NULL)) == NULL) { printf("Error XOpenDisplay\n"); return 1; }
win = XCreateSimpleWindow(dis, RootWindow(dis, 0), 1, 1, 256, 256, 0, BlackPixel(dis, 0), BlackPixel(dis, 0));
XSelectInput(dis, win, ExposureMask | KeyPressMask | KeyReleaseMask);
XMapWindow(dis, win);
XFlush(dis);
XEvent ev;
do {
XNextEvent(dis, &ev);
} while (ev.type != Expose);
GC gc;
gc = XCreateGC(dis, DefaultRootWindow(dis), 0, 0);
XSetFunction(dis, gc, GXxor);
while (!(XCheckWindowEvent(dis, win, KeyPressMask, &ev) || XCheckTypedWindowEvent(dis, win, ClientMessage, &ev))) {
XNextEvent(dis, &ev);
// quit program as soon as a key is pressed
if (ev.type == KeyPress && XEventsQueued(dis, QueuedAfterReading)) {
goto finish;
}
// I'd like to quit from this for loop as soon as a key is pressed
for (int t = 0; t < 150; t++) {
// quit program as soon as a key is pressed
if (ev.type == KeyPress && XEventsQueued(dis, QueuedAfterReading)) {
goto finish;
} else {
// draw a red oval and move it from left to right
XSetForeground(dis, gc, BlackPixel(dis, 0) ^ GetColor("red"));
XFillArc(dis, win, gc, t * 5 + 10, t * 3 + 40, 80, 40, 0, 360 * 64);
XSetForeground(dis, gc, BlackPixel(dis, 0) ^ GetColor("red"));
usleep(20000);
XFillArc(dis, win, gc, t * 5 + 10, t * 3 + 40, 80, 40, 0, 360 * 64);
//XSync(dis, True);
}
}
}
finish:
XFreeGC(dis, gc);
XDestroyWindow(dis, win);
XCloseDisplay(dis);
return(0);
}
I execute it with:
gcc draw.c -lX11 && ./a.out
The issue you are facing has to do with usleep.
Basically, usleep will suspend the thread that the program runs on until the set interval will finish. You can interrupt it by sending a signal (e.g. SIGKILL or SIGQUIT) to it, but this is something that can be done only outside of the program, not from within.
You may want to see this page to understand how usleep works.
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 would like to open a "dock type" window without its title bar with x11 in C and I want to capture a key press (Ctrl-q) to close it. The actual code does not capture the key presses to the active window as they are all sent and printed in the terminal. Almost all the Ctrl key presses are somehow not detected. The only key presses that actually work on the opened window are Ctrl-c. I am aware of the _MOTIF_WM_HINTS property but it has the disadvantage to display a window icon in the panel to show a new opened window.
How can I have a "dock type" window while not losing the ability to detect key presses to the active window?
Here is the code basically copied from two other posts here and here
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/keysym.h>
int main(int argc, char **argv)
{
Display *dpy = XOpenDisplay(0);
int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
200, 100, 0, blackColor, blackColor);
//Tell X Server to send MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
Atom window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
long value = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
XChangeProperty(dpy, w, window_type, 4, 32,
PropModeReplace,
(unsigned char *)&value, 1);
//Make window appear
XMapWindow(dpy, w);
//Graphics Context
GC gc = XCreateGC(dpy, w, 0, 0);
//Set white color for drawing
XSetForeground(dpy, gc, whiteColor);
//Wait for the MapNotify event
for(;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify) {
break;
}
}
//Draw the line
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
//Send the "DrawLine" request to the server
XFlush(dpy);
char text[255];
XEvent e;
KeySym key;
int numKeys = 0;
for(;;) {
XNextEvent(dpy, &e);
if(e.type == KeyPress) {
if((numKeys = XLookupString(&e.xkey, text, 255, &key, 0))) {
if(e.xkey.state == ControlMask && key == XK_q) {
printf("CTRL-Q\n");
break;
}
}
}
}
if (dpy && gc) XFreeGC(dpy, gc);
if (dpy && w) XDestroyWindow(dpy, w);
if (dpy) XCloseDisplay(dpy);
return 0;
}
Compiled with:
gcc main.c -L/usr/X11R6/lib -lX11 -lm && ./a.out
I've also looked at the _NET_WM_STRUT_PARTIAL property but I haven't been succesful at implementing it.
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.
This is a working example of X11 code that handles Ctrl-q event to quit application:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
void exitOnCondition(char cond, const char *msg, int exitCode, Display *dpy, Window *w, GC *gc) {
if(cond) {
printf("%s\n", msg);
if(dpy && gc) XFreeGC(dpy, *gc);
if(dpy && w) XDestroyWindow(dpy, *w);
if(dpy) XCloseDisplay(dpy);
exit(exitCode);
}
}
int main(int argc, char **argv) {
Display *dpy = XOpenDisplay(0);
exitOnCondition(dpy == 0, "Error: XOpenDisplay failed", -1, dpy, 0, 0);
int blackColor = BlackPixel(dpy, DefaultScreen(dpy));
int whiteColor = WhitePixel(dpy, DefaultScreen(dpy));
Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
200, 100, 0, blackColor, blackColor);
//Tell X Server to send MapNotify events
XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask);
//Make window appear
XMapWindow(dpy, w);
//Graphics Context
GC gc = XCreateGC(dpy, w, 0, 0);
//Set white color for drawing
XSetForeground(dpy, gc, whiteColor);
//Wait for the MapNotify event
for(;;) {
XEvent e;
XNextEvent(dpy, &e);
if (e.type == MapNotify) {
break;
}
}
//Draw the line
XDrawLine(dpy, w, gc, 10, 60, 180, 20);
//Send the "DrawLine" request to the server
XFlush(dpy);
char text[255];
XEvent e;
KeySym key;
int numKeys = 0;
for(;;) {
XNextEvent(dpy, &e);
if(e.type == KeyPress) {
//With modifier XLookupString will return garbage(?) in text[0] and key as latin1
if((numKeys = XLookupString(&e.xkey, text, 255, &key, 0))) {
printf("lookup returned:\n");
for(int i = 0; i < numKeys; i++) {
printf("text[%d]=%x\n", i, text[i]);
}
if(e.xkey.state == ControlMask && key == XK_q) {
exitOnCondition(1, "C-Q pressed", 0, dpy, &w, &gc);
}
}
}
}
XFreeGC(dpy, gc);
XDestroyWindow(dpy,w);
XCloseDisplay(dpy);
}
Will this code correctly handle Ctrl-q event on any system?
Can I use e.xkey.state to check for Ctrl modifier after XLookupString for any keyboard layout even when Ctrl is rebound to CAPS Lock(or anything else)?
Why does XLookupString return one symbol text[0]==0x11 for Ctrl-q event and not text[0]==CtrlModifierCode text[1]=='q'?
XLookupString returns a sequence of ISO-8859-1 characters (at least according to the manual I have; I do not know how up-to-date it is). There is no character code in ISO-8859-1 for the "ctrl" key by itself. Strictly speaking, there isn't one for the ctrl-q combination either, but tradition dictates that ctrl + (A...Z) map to the 1...26 range.