Ncurses text inside box flickering on refresh - c

I am trying to draw a box around some text that will be updated. In the place holder the text is static, but flickering is still observed. My code is below
/*
* #file main.cc
* #author Tom Eaton
* #date 09 Jan 2018
* #brief Command line interface to show Hawkcell PCB data.
*/
#include <iostream>
#include <string>
#include <cstring>
#include <ncurses.h>
using namespace std;
/*
* Prints string in the horizontal center of row specified.
* #param row - Row that text should be printed on
* #param mesg - Pointer to char array containing message
*/
void printHorizCenter(int row, char* mesg) {
mvprintw(row, ((COLS - strlen(mesg))/2)-1, mesg);
}
/*
* Reads version of PCB and updates char array with version
* #param outVersion - Pointer to char array where version should be stored.
* #param port - Reference to serial port
*/
void printHorizBlock(int row, int startCol, int endCol) {
for(int i = startCol; i<=endCol; i++) {
mvaddch(row, i, ACS_BLOCK);
}
}
int main(int argc, char** argv) {
//Initialise curses
initscr();
cbreak();
echo();
keypad(stdscr, 1);
//Make character capture non blocking
nodelay(stdscr, 1);
//Define char arrays for each cell
char cellOne[2] = {'1', '\n'};
char cellTwo[2] = {'2', '\n'};
char cellThree[2] = {'3', '\n'};
char cellFour[2] = {'4', '\n'};
//Setup curses colour schemes
start_color();
use_default_colors();
curs_set(0);
//Setup curses colour pairs
init_pair(1, COLOR_RED, -1);
init_pair(2, COLOR_WHITE, COLOR_RED);
WINDOW *win = newwin(10, 20, 1, 0);
//Main loop
while (1) {
if(getch() == 12) {
endwin();
cerr << "You pressed left and exited" << endl;
exit(1);
}
//Print top title bar
attron(COLOR_PAIR(1));
printHorizBlock(0, 0, (COLS/2) - (strlen("HawkCell")/2));
attroff(COLOR_PAIR(1));
attron(COLOR_PAIR(2));
printHorizCenter(0, (char *)"HawkCell");
attroff(COLOR_PAIR(2));
attron(COLOR_PAIR(1));
printHorizBlock(0, (COLS/2) + (strlen("HawkCell")/2) - 1, COLS);
attroff(COLOR_PAIR(1));
//Print voltage data
mvprintw(3, 1, "Cell 1: %.3fV\n", 4.0f);
mvprintw(4, 1, "Cell 2: %.3fV\n", 4.0f);
mvprintw(5, 1, "Cell 3: %.3fV\n", 4.0f);
mvprintw(6, 1, "Cell 4: %.3fV\n", 4.0f);
mvprintw(7, 1, "Total: %.3fV\n", 4.0f);
//Print bottom info bar
attron(COLOR_PAIR(1));
printHorizBlock(LINES-1, 0, COLS);
attroff(COLOR_PAIR(1));
attron(COLOR_PAIR(2));
printHorizCenter(LINES-1, (char *)"ctrl+c: Exit, ctrl+l: Toggle logging ");
attroff(COLOR_PAIR(2));
//Call curses refresh function to update screen
box(win, 0, 0);
//touchwin(win);
wrefresh(win);
wrefresh(stdscr);
}
getch();
endwin();
return 0;
}
The problem is that there is flickering when the screen is redrawn, shown in the gif below (Photo sensitivity warning)Gif showing problem. As you can see it only happens on the lines containing text, which makes me think that the whole line is being drawn when writing the text, not just the specific characters.
I have tried using a various combination of wrefresh(), touchwin(), erase() and refresh() but I couldn't find any that did not produce flickering.
How do I stop this flickering?

The problem is that your updates to stdscr and win alternate in updating the current screen (curscr), and since they write in the same area, they are doing a lot of repainting.
You could improve this by making a subwindow of win, and using that window for writing the text.
Something like
WINDOW *txt = derwin(win, 8, 18, 1, 1);
... modify all of the calls using stdscr:
mvwprintw(txt, 7, 1, "Total: %.3fV\n", 4.0f);
//Print bottom info bar
wattron(txt, COLOR_PAIR(1));
and using wgetch(txt) rather than getch() to get characters. The box only needs to be refreshed once (before the loop), and the wgetch() refreshes the window anyway: remove the last wrefresh and move the other right after creating win and calling box:
WINDOW *win = newwin(10, 20, 1, 0);
box(win, 0, 0);
wrefresh(win);

Related

new_menu() fails when then menu has an odd number of items (!=1)

I'm trying to create a menu in ncurses and the number of items in the menu can change on every call to my function, but if I run this when I have (3,5) unique items in the menu, I get a crash and gdb gives me this:
_nc_Connect_Items error ncurses _nc_Connect_Items (menu=menu#entry=0x5555555f2830, items=items#entry=0x5555555e0b70) at ../menu/m_global.c:185
I can post full output if needed.
Another problem I have is the description string in the menu being the same as last item, but maybe it's WAI. Could not find it in the docs at https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/.
So here's the code:
...
WINDOW *my_wins[1];
ITEM **my_items;
MENU *my_menu;
WINDOW *my_menu_win;
int n_choices = 0;
int c;
/* Initialize curses */
setlocale(LC_CTYPE, "uk_UK.UTF-8");
initscr();
clear();
refresh();
start_color();
cbreak();
noecho();
keypad(stdscr, TRUE);
/* Initialize all the colors */
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(3, COLOR_BLUE, COLOR_BLACK);
init_pair(4, COLOR_CYAN, COLOR_BLACK);
init_pair(5, COLOR_WHITE, COLOR_BLACK);
init_pair(6, COLOR_YELLOW, COLOR_BLACK);
init_pair(7, COLOR_BLACK, COLOR_WHITE);
init_pair(8, COLOR_MAGENTA, COLOR_BLACK);
//To print something when you have no consumables
for (int i = 0; i < CONSUMABLESMAX +1; i++) {
Consumable * c = &consumablesBag[i];
int q = c->qty;
if (q > 0) {
n_choices++;
}
}
if (n_choices == 0) {
my_menu_win = newwin(10, 40, 2, 2);
box(my_menu_win, 0, 0);
wattron(my_menu_win,COLOR_PAIR(6));
mvwprintw(my_menu_win,2, 2, "Your bag is empty.");
mvwprintw(my_menu_win,3, 2, "You'll find more consumables soon.");
wattroff(my_menu_win,COLOR_PAIR(6));
wrefresh(my_menu_win);
refresh();
screenTime(3);
endwin();
return;
}
/* Create menu items */
int tot = 0;
int q = 0;
my_items = (ITEM **)calloc(n_choices,sizeof(ITEM *));
for (int i = 0; i < CONSUMABLESMAX +1; i++) {
if (tot < n_choices) {
Consumable * c = &consumablesBag[i];
q = c->qty;
if ( q > 0) {
char qty[15];
sprintf(qty, "x%i", q);
my_items[tot] = new_item(consumablestrings[i], qty);//consumablestrings[i]);
tot++;
}
} else {
break;
}
}
/* Create menu */
my_menu = new_menu((ITEM **)my_items);
/* Set description off */
//menu_opts_off(my_menu,O_SHOWDESC);
I tried to debug the array indexing but it seems to work, you have it working on 2 and 4 items... If someone could help I'd greatly appreciate it.
Edit:
After #WeatherVane commented I checked the docs again and in the examples there was always a null pointer ending the items array for the menu, so the crash was resolved by putting
my_items[tot] = (ITEM*) NULL;
just before the call to new_menu().
This solves the main problem, but the description string for the menu items is still not correct, as they are all identical, taking the value of the last item on the list. I can't seem to find docs on how this is handled by ncurses, but maybe I should have a separate question for this other problem.

The problem of printing characters to the right in a for loop with the ncurse library

I use Xshell to connect to a remote host and write ncurse programs. But it was found that the characters could not be printed in a cycle to the right.
#include <ncurses.h>
int main() {
initscr();
for (int i = 0;i < 10; i++){
addch('o');
}
refresh();
getch();
endwin();
return 0;
}
Result:
Only the first character is printed.
In order to print it all out, I had to add the refresh code
for (int i = 0; i < 10;i++) {
addch('o');
refresh();//print all 'o'
}
I think the BIG problem makes the box function unavailable, because the two Horizontal line of a box are not completed, it only have the first '-' too.
int main() {
initscr();
curs_set(0);
WINDOW *win;
win = newwin(10, 40, 9, 20);
box(win, 0, 0);
refresh();
wrefresh(win);
getch();
endwin();
return 0;
}
Result:
Notice the two horizontal lines up and down
I can't figure out this problem.
2021.3.11
I have I have identified the problem. The problem is with Xshell. I connect my host by Alibaba Cloud Server official website, and the program have no problem. But I still don't konw how to set up my xshell.
Using the following code (save to test.c and compile with gcc -o test test.c -lncurses) I can draw a border, put text inside it, and also put text in the main window.
#include <ncurses.h>
// Print the character 'ch' in the window
// 'win', and do this 'repeat' times.
int print_chars(WINDOW *win, char ch, int repeat) {
if (win == NULL) {
// Print to main window
for (int i=0; i<repeat; i++) {
addch(ch);
refresh();
}
} else {
// Print to the named window
for (int i=0; i<repeat; i++) {
waddch(win, ch);
}
wrefresh(win);
}
return 0;
}
int main(int argc, char **argv)
{
WINDOW *border_win, *win;
initscr();
refresh(); // This seems to be needed;
// I don't know why.
// Create a window to show the border
border_win = newwin(10,40,9,20);
box(border_win,0,0);
wrefresh(border_win);
// Create a window inside that, which
// we will write in. Otherwise it
// seems we will overwrite the border.
win = newwin(8,38,10,21);
print_chars(win, 'a', 10);
// Write something in the main window
// as well, just to show it works
print_chars(NULL, 'o', 10);
getch();
endwin();
return 0;
}

How can i add line breaks when rendering Text using X11,

I am making an application that renders texts according to style mentioned on screen using X Windows System and Xft. My code is working fine as shown below.
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
char * nowShowing()
{
return strdup("This is a sample text This is rendered with new Driver installed This is a sample text his is rendered with new Driver installed");
}
int main()
{
XftFont *font;
XftDraw *draw;
XRenderColor render_color;
XftColor xft_color;
char *str;
str = nowShowing();
int x = 70;
int y = 150;
Display *dis = XOpenDisplay (0);
int screen = DefaultScreen (dis);
Window w = XCreateSimpleWindow (dis, RootWindow (dis, screen),
0, 0, 1200, 300, 1,
BlackPixel (dis, screen),
WhitePixel (dis, screen));
XEvent ev;
render_color.red = 0;
render_color.green =0;
render_color.blue = 0;
render_color.alpha = 0xffff;
XftColorAllocValue (dis,
DefaultVisual(dis, screen),
DefaultColormap(dis, screen),
&render_color,
&xft_color);
//font = XftFontOpen(dis, screen,
// XFT_FAMILY, XftTypeString, "charter",
// XFT_SIZE, XftTypeDouble, 20.0,
// NULL);
font = XftFontOpenName(dis,screen,"URW Palladio L:style=Bold Italic"); //it takes a Fontconfig pattern string
draw = XftDrawCreate(dis, w,
DefaultVisual(dis, screen),
DefaultColormap(dis, screen));
XSelectInput (dis, w, ExposureMask);
XMapWindow (dis, w);
for (;;)
{
XNextEvent (dis, &ev);
if (ev.type == Expose)
XftDrawString8(draw, &xft_color, font, x, y, (XftChar8 *) str,
strlen(str));
}
return 0;
}
But i am wondering that how can i add line breaks in the text entered. I tried using "/n" and also tried to make array and used loops but it didn't work.
New line "\n" will not be rendered by Xft. You need to render each line separately with proper offset, depending on font size and desired spacing.
I have modified the ending block of your code with sample text rendered two times on separate lines.
if (ev.type == Expose)
{
int fonth = font->ascent + font->descent;
XftDrawString8(draw, &xft_color, font, x, y, (XftChar8 *) str,
strlen(str));
XftDrawString8(draw, &xft_color, font, x, y+fonth, (XftChar8 *) str,
strlen(str));
}

Basic Ncurses Menu

Im trying to do a basic menu in C. I'm supposed to do this with ncurses lib. I was working with this tutorial:
Video On YouTube
But mine version has some problems:
1)The menu will not print properly, it will reveal only while choosing menu items. Then the highlight won't go off
2)Option made on menu won't print on the top
Can you help me? Is that idea of Menu good or should i look for other tutorial (any help ?).
#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <menu.h>
int main(int argc, char **argv)
{
int i, c;
char powitanie[]="SLOWNIK UNIWERSALNY";
int szer, dlug; //wartosci dlugosci i szerokosci terminalu
initscr(); //Inizjalizacja całości ncurses, kolory itp
raw();
noecho();
keypad(stdscr, TRUE);
start_color();
//init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
//attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
printw(powitanie); //Drukujemy powitanie
//attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
refresh();//Odswiezamy (inaczej się nie wyswietli)
WINDOW * menuwin=newwin(6, dlug-12, szer-8, 6); //Definiujemy i tworzymy 'okno'
box(menuwin, 0, 0);
refresh();//ponownie odświeżamy aby okno się pojawiło
wrefresh(menuwin);//odświeżamy samo okno
keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
char *opcje[] = {
"Tlumacz z Polskiego na Angielski",
"Tlumacz z Angielskiego na Polski",
"Edystuj slownik",
"Wybierz slownik",
"Wyjdz",
};
int wybor;
int zaznacz=0;
while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
{
for(i=0; i<5; i++)
{
if(i==zaznacz)
{
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, opcje[i]);
wattroff(menuwin, A_REVERSE);
}
wybor = wgetch(menuwin);
switch(wybor)
{
case KEY_UP:
zaznacz--;
if(zaznacz==-1) zaznacz=0;//zabezpieczenie przed wyjsciem "poza" menu
break;
case KEY_DOWN:
zaznacz++;
if(zaznacz==5) zaznacz=4;
break;
default:
break;
}
if(wybor==10) break;
}
printw("Wybrano:%s", opcje[zaznacz]);
}
return(0);
}
PS: Code comments are not in English but i hope the won't be necessary
There are quite a few problems here. I have included a modified version of your code that works, and I will attempt to describe the changes.
There were some unused variables, namely argc, argv, and c, so I cast these to void in order to silence compiler warnings. You can remove the c and change to int main(void), if you like, removing these variables altogether.
I have added the stdlib.h header file to your #includes for the exit() function. This is used in the new error function, fail(), that I added to your code. You should always check the return values of any function that you call when programming in C. Here it is particularly important to check, first if the terminal supports color with the has_colors() function, and then if the call to start_color() is successful. If either of these fail, the fail() function is called with an error message, and the program exits with the EXIT_FAILURE value. The function has_colors() returns a bool, and the start_color() function returns an int (OK if successful, otherwise ERR).
Now that colors have been initialized, I see that the lower border of your menu selection window is being overwritten by the menu text. To fix this, I changed the size of your window, making it one line taller:
WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6);
The fundamental problem of improper printing that you reported was because of a misplaced brace in the for loop controlling the printing of the menu items. I took the opportunity to reorganize the loop a bit; now there is only one call to mvwprintw(). The A_REVERSE attribute is set before printing if the current item is also the selected item, and it is again unset after printing.
I also changed the limit tests in the switch statement from equalities to inequalites. It is better practice to use , e.g., if (zaznacz < 0) instead of if (zaznacz == -1) in such cases.
I added a newline character to the beginning of the format string in the final printw(), since some of the selections are too long to fit in the window at the end of the title. You can move this output wherever you like.
Finally, I added a refresh() after the final printw() statement, and a getch() to wait for the user to hit ENTER before exiting the program. It is very important to cleanup by calling endwin() before exiting an NCurses program. This function reverses changes made to your terminal by NCurses while your program was running, and failure to do this can lead to terminal unpleasantries.
#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <menu.h>
#include <stdlib.h> // added for exit() function
void fail(char *msg) {
endwin();
puts(msg);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
/* Commandline argument currently unused */
(void) argc;
(void) argv;
int i, c;
(void) c; // c is currently unused
char powitanie[]="SLOWNIK UNIWERSALNY";
int szer, dlug; //wartosci dlugosci i szerokosci terminalu
initscr(); //Inizjalizacja całości ncurses, kolory itp
raw();
noecho();
keypad(stdscr, TRUE);
/* Test to see if terminal has colors */
if (has_colors() == false) {
fail("Colors unavailable\n");
}
if (start_color() != OK) {
fail("Unable to start colors\n");
}
//init_pair(1, COLOR_BLUE, COLOR_BLACK); //wybór kolorów
getmaxyx(stdscr, szer, dlug); //pobranie rozmiarów terminalu
move(szer/2, (dlug-strlen(powitanie))/2); //przesuwamy kursor na środek (tak aby się ładnie wydrukowało)
//attron(COLOR_PAIR(1)); //Aktywujemy wybrane kolory
printw(powitanie); //Drukujemy powitanie
//attroff(COLOR_PAIR(1));//Dezaktywujemy kolory
refresh();//Odswiezamy (inaczej się nie wyswietli)
WINDOW * menuwin=newwin(7, dlug-12, szer-9, 6); //Definiujemy i tworzymy 'okno'
box(menuwin, 0, 0);
refresh();//ponownie odświeżamy aby okno się pojawiło
wrefresh(menuwin);//odświeżamy samo okno
keypad(menuwin, TRUE);//umozliwiamy dzialanie klawiatury w oknie
char *opcje[] = {
"Tlumacz z Polskiego na Angielski",
"Tlumacz z Angielskiego na Polski",
"Edystuj slownik",
"Wybierz slownik",
"Wyjdz",
};
int wybor;
int zaznacz=0;
while(1)//cala ta petla sluzy ciaglemu tworzeniu menu z podswietleniem wybranego elementu
{
for(i = 0; i < 5; i++) {
if(i == zaznacz)
wattron(menuwin, A_REVERSE);
mvwprintw(menuwin, i+1, 1, opcje[i]);
if (i == zaznacz)
wattroff(menuwin, A_REVERSE);
}
wybor = wgetch(menuwin);
switch(wybor)
{
case KEY_UP:
zaznacz--;
if(zaznacz < 0) zaznacz = 0;//zabezpieczenie przed wyjsciem "poza" menu
break;
case KEY_DOWN:
zaznacz++;
if(zaznacz > 4) zaznacz = 4;
break;
default:
break;
}
if(wybor==10) break;
}
printw("\nWybrano:%s", opcje[zaznacz]);
refresh();
/* Wait for user to press enter to exit */
getch();
/* Need to cleanup before exit */
endwin();
return 0;
}

move(y,x) in ncurses in GNU C on Linux

I have a the following piece of code using ncurses. I would like to know whether I can use a single move function to print a few lines.
For Example:
move(25,25);
printw("Line 1\n");
printw("Line 2\n");
Line 1 prints at (25,25) location but Line 2 prints at (26,0) if I don't use move(26,25). Can I avoid the second move and still print Line 2 at (26,25)????
You can define a new window if what you want to print has to be aligned. Shortly :
#include <ncurses.h>
int main()
{
WINDOW* mywin;
initscr();
cbreak();
keypad(stdscr, TRUE);
int height=15;
int width=30;
int starty=25;
int startx=25;
printw("F9 to exit");
refresh();
mywin = newwin(height, width, starty, startx);
mvwprintw(mywin,0,0,"First line\n");
wprintw(mywin,"Second line");
wrefresh(mywin);
while(getch() != KEY_F(9)) {}
endwin();
return 0;
}
If this approach does not fit, then you'll have to move manually to next position you want to print.

Resources