I’m new to programming and I’d like to realise the following with ncurses in C:
A form with fields to fill out and underneath this form, there is a continuously changing sensor value to be observed by the user during filling out the form, which results in the desired actions.
I’m glad I made it that far, that I can put the field buffer into my variables now, by pressing return, but now I’m facing a problem which seems to be not googleable.
My program started off from the example, which I posted underneath. In the original example I just added two lines and it already demonstrates my problem very well.
I set timeout(1); so the getch() function won’t wait for user input in the form before it prints the fresh sensor values.
Into the while-loop I put in the sennsor value with mvprint.
Now the sensor values are always up-to-date and it is still possible to move from one field to the other with the arrow keys and type into the fields.
But the visible Cursor always stays at the sensor value, which makes sense to me, because it is continuously moved there for printing. The forms driver seems to remember the position which was edited lastly, so that editing the fields will still function, but without any optical hint at which position the typing will be. The documentation refers to this position as the “editing-cursor” at one point.
Am I doing something completely wrong? Or is there a way to highlight the field, or even make the editing-cursor visible?
Thank you!
/* gcc -Wall -pthread -g -o formncurses formncurses.c -lform -lncurses */
#include <form.h>
int main()
{ FIELD *field[3];
FORM *my_form;
int ch;
/* Initialize curses */
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
timeout(1);
/* Initialize the fields */
field[0] = new_field(1, 10, 4, 18, 0, 0);
field[1] = new_field(1, 10, 6, 18, 0, 0);
field[2] = NULL;
/* Set field options */
set_field_back(field[0], A_UNDERLINE); /* Print a line for the option */
field_opts_off(field[0], O_AUTOSKIP); /* Don't go to next field when this */
/* Field is filled up */
set_field_back(field[1], A_UNDERLINE);
field_opts_off(field[1], O_AUTOSKIP);
/* Create the form and post it */
my_form = new_form(field);
post_form(my_form);
refresh();
mvprintw(4, 10, "Value 1:");
mvprintw(6, 10, "Value 2:");
refresh();
/* Loop through to get user requests */
while((ch = getch()) != KEY_F(1))
{ switch(ch)
{ case KEY_DOWN:
/* Go to next field */
form_driver(my_form, REQ_NEXT_FIELD);
/* Go to the end of the present buffer */
/* Leaves nicely at the last character */
form_driver(my_form, REQ_END_LINE);
break;
case KEY_UP:
/* Go to previous field */
form_driver(my_form, REQ_PREV_FIELD);
form_driver(my_form, REQ_END_LINE);
break;
default:
/* If this is a normal character, it gets */
/* Printed */
form_driver(my_form, ch);
break;
}
mvprintw(12, 10, "Here stands the changing sensor value");
}
/* Un post form and free the memory */
unpost_form(my_form);
free_form(my_form);
free_field(field[0]);
free_field(field[1]);
endwin();
return 0;
}
The getch call essentially tells ncurses to leave the cursor where your mvprintw has left it — on the standard screen. To get it to move to your form, you would have to tell it to use wgetch, passing the WINDOW* pointer for the current form (which in turn holds a window's position for the field).
Further reading:
FIELD *current_field(const FORM *);
WINDOW *form_win(const FORM *form);
One thing that #Thomas's answer does not provide is how to highlight the active field (as indicated by the title of the question). For completion's sake, I am going to add a function here that will allow you to do just that:
void highlight_current_field(FORM *form, FIELD *fields[])
{
FIELD *cfield = current_field(form);
FIELD *currfield;
int i = 0;
while ((currfield = fields[i]) != NULL)
{
if (currfield == cfield)
set_field_back(currfield, A_STANDOUT);
else if (i >= 4)
set_field_back(currfield, A_UNDERLINE);
++i;
}
}
I hope this helps fellow travelers. For more information on the attributes provide, check out this site.
Related
I'm fairly new to C (high school student), and my goal is to make a grocery list by inputting a set of characters. Then, the output would print out what I currently added to the list. This would go on forever until I exited the program or went to the main menu.
SCREEN1
00 - GO TO GREETINGS SCREEN
01 - ADD MORE ITEMS TO THE LIST
CODE ENTRY: ___
SCREEN2
Then I entered 01 to add items:
Input "DONE" to end program and see final list.
LIST ENTRY: ______________
SCREEN3
Then I add "apples", then it takes me to this screen:
GROCERY LIST:
POTATOES
FISH
APPLES
After that, it takes me back to SCREEN1, where I would choose whether to go to the greetings screen or add some more.
MY CURRENT CODE:
#include <stdio.h>
#include<stdlib.h>
int main()
{
int n;
char * grocery;
mainMenu:
system("cls");
n = 0;
printf("00 - Go to greetings screen\n01 - Add groceries to list\nENTRY: ");scanf("%d",&n);
if(n == 0)
{
greetings();
goto mainMenu;
}
else if(n == 1)
{
printf("GROCERY ENTRY: ");scanf("%s",grocery);
add(grocery);
goto mainMenu;
}
else
{
printf("Wrong value added. Try again.");
sleep(2);
goto mainMenu;
}
}
void greetings()
{
system("cls");
printf("hello! press any key to go back to menu");
getch();
system("cls");
}
void add(char * a)
{
system("cls");
char listData[1000] = "",slashN[4] = "\n";
strcat(listData,a);
strcat(listData,slashN);
printf("THINGS TO BUY:\n");
puts(listData);
}
NOTES
I used strcat so that it remembers the value of the original string. This'll make sure that the new input will just be put on top of the old data, right? (Hopefully, my logic is correct on that one)
Though, I have not been able to find out whether or not the string data will still be remembered by the program even if I am switching to other menus, such as the Greetings Menu (which is a placeholder for something like a calculator).
Right now, the initial functions work, except for the the grocery list one. Whenever I input a string, nothing comes out of it, even the printf("THINGS TO BUY:") part, leading me to think it's something with what I entered in void add(char * a)
After trying out your code, I received a segmentation fault where you have scanf("%s",grocery);. Try allocating space to the variable, using malloc(3) (see http://man7.org/linux/man-pages/man3/malloc.3.html). For example, char *grocery = (char *)malloc(sizeof(char) * 100); will give you 100 characters to read into your grocery variable from input.
The same objective can also be achieved by simply using char grocery[100]; as you have with other variables.
Furthermore, as many programmers would suggest, try not to use goto(). This can cause spaghetti code (see https://en.wikipedia.org/wiki/Spaghetti_code). Instead, use a while loop that stops only when the user inputs some action designed to stop entering requests.
Lastly, just so you avoid unwarranted results, \n is actually a single character. Therefore, char slashN[4] = "\n" can be char slashN = '\n';.
Happy coding! :D
I am making a project in C. Its simple, just a Hangman Game.
Got the logic already cause I've done that only in console.
Now, I'm trying to do it in C again with GRAPHICS. I am using Turbo C.
I've read some of the functions of graphics.h: so far I've seen outtext() / outtextxy() something like that. It can print a string.
Can you input a char or string in graphics? Searched a lot but seen nothing.
Seen only drawing shapes examples.
How do you input characters, integers etc. in the graphics mode?
From memory, while you can use regular stdio functions printf, scanf, and gets, the graphics driver will paint them over your screen onto a "virtual cursor" position and scroll the screen when it reaches the bottom. You can use the nonstandard conio.h functions such as gotoxy and attempt to position the cursor, but it's still a mediocre way of inputting text, messing up the graphics stuff. You also cannot use the fancy fonts!
So use getch to read characters without showing them; update a string buffer (manually handling special keys such as Backspace and Return), and draw that on the screen using a font of your choice.
A short sample snippet of code to get you started:
#define MAX_INPUT_LEN 80
char inputbuf[MAX_INPUT_LEN];
int input_pos = 0;
then, in your main loop
int the_end = 0;
do
{
outtextxy (0,0, inputbuf);
c = getch();
switch (c)
{
case 8: /* backspace */
if (input_pos)
{
input_pos--;
inputbuf[input_pos] = 0;
}
break;
case 13: /* return */
the_end = 1;
break;
case 27: /* Escape = Abort */
inputbuf[0] = 0;
the_end = 1;
break;
default:
if (input_pos < MAX_INPUT_LEN-1 && c >= ' ' && c <= '~')
{
inputbuf[input_pos] = c;
input_pos++;
inputbuf[input_pos] = 0;
}
}
} while (!the_end);
Before you draw the text, make sure to erase the previous line! I left that out because it's been too long ago I used Turbo-C.
For taking input from used.. you can use scanf function similar to how we take input in any non-graphics c program.
You can do this by using normal scanf function. And by using sprintf copy a integer/whatever variable into a string. Now you can use outtextxy to set the string in specified location using x,y axis. Check geeksforgeeks to learn sprintf syntax.
I know it is too late. Hope it helps someone else.!
The program below uses a keypad and arduino to test whether an input is equal to the password. Whenever '#' is pressed the input is checked and then the variable storing the input resets to an empty string (char input[257] = ""). The program then loops back to the beginning of the void loop() code block. I am having a problem when my program loops back to the beginning of the "void loop()" code block. When i reset input to an empty string, input is reset to an empty string as it should. However, when the program loops the value of input is changed to what it was before. So if i originally entered "123abc" and hit '#' the program would tell me that the input was incorrect and then the program would reset the variable to an empty string, but when the program loops the variable storing the empty string is changed back to "123abc". What's happening? Why doesn't the variable remain an empty string?
#include <Keypad.h>
const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {9, 8, 7, 6}; //connect to the column pinouts of the keypad
char password[9] = "3994A", input[257]="";
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
int x;
void setup(){
Serial.begin(9600);
}
void loop(){
x = 0;
Serial.println(input);
while (1)
{
char key = keypad.getKey();
if (key)
{
if (key == '#')
{
break;
}
input[x] = key;
x+=1;
}
}
if (strcmp(password,input) == 0)
{Serial.println("Access Granted");
}
else
{Serial.println("Access Denied");
}
char input[257] = "";
Serial.println(input);
}
Not the same variable. You've got one input in your top block and another in your loop function.
This line
char input[257] = "";
makes a new local variable called input, but you dont want that. You already made a global one here:
char password[9] = "3994A", input[257]="";
Change
char input[257] = "";
to
memset(input,0,257);
So you dont lose the new variable off the stack and instead use the global one you declared earlier.
The second char input[257] = ""; declares a new variable named input. It does not change the contents of the existing variable named input. Use memset() instead.
I agree with the error in declaring again the input variable
char input[257] = "";
but i would not solve it with memset() but doing
input[0]='\0';
because this is faster and doesn't require to include the memset() function on the code. we are talking about a microcontroller, cpu and memory is precious and it is a good habit to write fast and light code.
Here, how could I achieve this:
Go to the official Arduino Website Download Keypad.h http://playground.arduino.cc/Code/Keypad#Download (ctrl+f and type: download) Download the library to put on C:\Program Files (x86)\Arduino\libraries Close your current Arduino screen then re-open the screen. Rewrite your code and put the code first. I okey with this method.
#include <Keypad.h>
I write a client for a console game, 1vs1. In the game one player have to catch the other, and every player is rappresented with a char, printed on the console. I use the mvaddch() to delete the old position, and to print the new position on the console.
My code generate 2 process:
Process A: It get the input from the keyboard and update the position on the screen;
Process B: it get the input from the server and update the position of the enemy on the screen;
My problem is the old position of the enemy is not deleted (overwriting with ' '), and so process B generates a snake of charactes on the screen. Process A work good.
initscr();
noecho();
curs_set(0);
//process A is created now
switch ( pid = fork() ) {
case -1: perror("fork() fallita"); exit(0);
case 0: {char c; struct pos old_position = {welcome_position.c, welcome_position.x, welcome_position.y};
struct pos position = {welcome_position.c, welcome_position.x, welcome_position.y};
mvaddch(position.y, position.x, position.c);
while (1) {
switch(c=getch()) {
case SU: if(position.y>0) { position.y-=1; } break;
case GIU: if(position.y<MAXY-1){ position.y+=1; } break;
case SINISTRA: if(position.x>0){ position.x-=1; } break;
case DESTRA: if(position.x<MAXX-1){ position.x+=1; } break;
default: break; }
if ((position.x != old_position.x) || (position.y != old_position.y)) {
send(sock, &position, sizeof(struct pos), 0);
mvaddch(old_position.y, old_position.x, ' ');
mvaddch(position.y, position.x, position.c);
refresh();
old_position.x = position.x;
old_position.y = position.y; }} }
default: break ; }
// Process B is here
struct pos position;
struct pos old_position={' ', -1,-1};
while (1) {
while ( recv(sock, &position, sizeof(struct pos), 0) < 1 )
mvaddch(old_position.y, old_position.x, ' '); // THE PROBLEM
mvaddch(position.y, position.x, position.c); // Works => snake
refresh();
old_position.x = position.x;
old_position.y = position.y;}
endwin();
kill(pid);
printf("\n-----------------------------\n");
}
If you don't want the entire trail showing, you have to keep a record of the previous position of each character (player) and arrange to write a blank at the old position and the correct mark at the new position. If you're feeling fancy, you might use a coloured blank, one colour for each player, so you can see where each has been, even though the current positions are marked differently.
Unfortunately, without the colouration mentioned, that looks like what you're doing.
You should make sure you don't use the -1 coordinates; mvaddch() is probably not error checked and will go trampling out of bounds, doing who knows what damage. Don't risk it. (Consider using 0, 0 as the old position; it won't matter if you write a blank over a blank. The only thing that matters is that the other player is not where you write the blank.)
Note that it is crucial that only one process is doing the drawing. If you have two processes attempting to do so, you will lose one or the other images some of the time. This is one reason reason why it is hard to add a clock, say, to the top right corner of a terminal screen. You do seem to have two processes trying to write to the screen.
Style-wise, you need to use more functions. That is, the different processes should each have their code in a separate function, so that it is easier to see what they are doing. Stacking close braces } three deep on a single line is not good style either.
I was writing a command line program which will have a status bar, much like wget.
The main problem I'm facing is: how do I delete what I've already sent into stdout/stderr?
I had on idea: use the backspace char '\b' and erase the output I've sent. Is that the best way? Is it the only way? Is there a better way?
PS: I don't want to use anything like ncurses. Plain old C please.
Thanks
EDIT:
Can I also go up and/or down? Example: I have 10 lines of output, I want to change the 3rd line from Doing ABC to ABC: Done. How can I do that?
Also, can anyone post more details about what VT102 characters are? What are its capabilities? Please post good links on this if you have any.
Thanks
The basic formatting control characters are backspace (\b), tab (\t), newline (\n), and carriage return (\r). If you need more than that then you can use ANSI X3.64 / ISO/IEC 6429 / ECMA-48 escape sequences; at least the VT100 subset is recognized by most modern terminals and emulators. An advantage of using ncurses is that it will look up the capabilities of your particular terminal and so it will work even if your terminal uses a different set of escape sequences.
You have to remember that as far as the regular stdio routines are concerned, stdout is just a byte stream with no inherent display characteristics; that depends on the target device, which can be anything from a regular VT100-style terminal to a hardcopy terminal to a sheet-fed printer to a plotter to whatever.
IMO, you're far better off using a library like ncurses than trying to hack together your own display management code with VT100 escape codes, even for a relatively simple task like this. I know you want to stick with "plain old C", but this is a task that falls outside the bounds of plain old C.
Use '\r' to return to the beginning of the line and possibly rewrite the whole line.
Look for VT102 control sequences - these are character sequences ESC ... to control your terminal.
There is also the possiblity of using Ncurses, which is a library for Textual UI, where this kind of behaviour should have some support. However, it may be overkill for something like this.
A slight variation on your own solution:
You can also print a carriage return (\r), which will return you to the start of the line.
It is a progressbar for bash.
function gauge()
{
progress="$1"
total="$2"
width=`tput cols`
let gwidth=width-7
if [ "$total" == "0" ]; then
percent=100
else
set +e
let percent=progress*100/total;
set -e
fi
set +e
let fillcount=percent*gwidth/100
let nofillcount=gwidth-fillcount
set -e
fill="";
if [ "$fillcount" -gt "0" ]; then
for i in `seq $fillcount`; do
fill="$fill""|"
done
fi;
nofill=""
if [ "$nofillcount" -gt "0" ]; then
for i in `seq $nofillcount`; do
nofill="$nofill"" ";
done
fi
echo -e -n "\r[""$fill""$nofill""] ""$percent""%";
}
About the progress bar: something like this?
#include <stdio.h>
#include <unistd.h>
typedef enum
{
false=0,
true=!false
} bool;
typedef struct
{
/* Start delimiter (e.g. [ )*/
char StartDelimiter;
/* End Delimiter (e.g. ] )*/
char EndDelimiter;
/* Central block (e.g. = )*/
char Block;
/* Last block (e.g. > ) */
char CurBlock;
/* Width of the progress bar (in characters) */
unsigned int Width;
/* Maximum value of the progress bar */
double Max;
/* True if we have to print also the percentage of the operation */
bool PrintPercentage;
/* True if the bar must be redrawn;
note that this must be just set to false before the first call, the function then will change it by itself. */
bool Update;
} ProgressBarSettings;
/* Prints/updates the progress bar */
void PrintProgressBar(double Pos, ProgressBarSettings * Settings);
/* Inits the settings of the progress bar to the default values */
void DefaultProgressBar(ProgressBarSettings * Settings);
int main()
{
int i;
/* Init the bar settings */
ProgressBarSettings pbs;
DefaultProgressBar(&pbs);
pbs.Max=200;
pbs.Width=60;
printf("Progress: ");
/* Show the empty bar */
PrintProgressBar(0,&pbs);
for(i=0;i<=pbs.Max;i++)
{
/* Wait 50 msec */
usleep(50000);
/* Update the progress bar */
PrintProgressBar(i,&pbs);
}
puts(" Done");
return 0;
}
/* Inits the settings of the progress bar to the default values */
void DefaultProgressBar(ProgressBarSettings * Settings)
{
Settings->StartDelimiter='[';
Settings->EndDelimiter=']';
Settings->Block='=';
Settings->CurBlock='>';
Settings->PrintPercentage=true;
Settings->Update=false;
Settings->Max=100;
Settings->Width=40;
}
/* Prints/updates the progress bar */
void PrintProgressBar(double Pos, ProgressBarSettings * Settings)
{
/* Blocks to print */
unsigned int printBlocks=(unsigned int)(Settings->Width*Pos/Settings->Max);
/* Counter */
unsigned int counter;
/* If we are updating an existing bar...*/
if(Settings->Update)
{
/* ... we get back to its first character to rewrite it... */
for(counter=Settings->Width+2+(Settings->PrintPercentage?5:0);counter;counter--)
putchar('\b');
}
else
Settings->Update=true; /* next time we'll be updating it */
/* Print the first delimiter */
putchar(Settings->StartDelimiter);
/* Reset the counter */
counter=Settings->Width;
/* Print all the blocks except the last; in the meantime, we decrement the counter, so in the end we'll have
the number of spaces to fill the bar */
for(;printBlocks>1;printBlocks--,counter--)
putchar(Settings->Block);
/* Print the last block; if the operation ended, use the normal block, otherwise the one for the last block */
putchar((Settings->Max==Pos)?Settings->Block:Settings->CurBlock);
/* Another block was printed, decrement the counter */
counter--;
/* Fill the rest of the bar with spaces */
for(;counter;counter--)
putchar(' ');
/* Print the end delimiter */
putchar(Settings->EndDelimiter);
/* If asked, print also the percentage */
if(Settings->PrintPercentage)
printf(" %3d%%",(int)(100*Pos/Settings->Max));
/* Flush the output buffer */
fflush(stdout);
};
Note: the unistd.h and usleep thing is just to fake the progress of an operation, the progress bar code itself just uses the standard library. Its only assumptions about the output stream are that \b actually gets to the previous written character. I tried it successfully on Windows and Linux (with gnome-terminal), don't know if it doesn't work correctly with some terminal emulators.
Sorry for the excessive amount of comments, I wrote it for another forum where I needed to explain pratically every line of the code to a C newbie.