Hi, I've been trying to analyze the source code to some applications but so far I've been unable to figure out how to receive user input while the program is of doing something else.

My program consists of a small list of items which are being constantly sorted. The problem is, receiving user input the "traditional" way cause the whole thing to stop completely until the user presses "enter". I want to do it in a way the program keeps going UNTIL the user hits 'c', for instance.

Recommended Answers

All 20 Replies

you need to create another thread so that user input is in the main thread and other processing is done in the other thread.

I already expected that. But from what I've heard, there's other ways to do it... (No one seems to remember how for some reason though)

you need to create another thread so that user input is in the main thread and other processing is done in the other thread.

Is there any reference I can use for doing that? The only source I can study comes from the linux "top" and having things I can't understand in the middle of a program who deals intimately with the os... :p

>>there's other ways to do it... (No one seems to remember how for some reason though)

If you are running MS-DOS or MS-Windows some compilers have console functions in conio.h that will help -- _kbhit() tests if a key is available at the keyboard and getche() gets one character. Put those in a loop and you can do other processing while there are no keys available at the keyboard.

use those libraries (conio.h, windows.h, etc.) at you're own peril. they offer quick solutions, but are completely non-portable. you'd do much better for yourself learning to write a multi-threaded solution.

While the libraries are non-portable, don't worry too much about using _kbhit() or some other like function. If you do need to port someday, it is trivial to implement it in terms of the new platform.

While jephthah is correct that you are better off just learning to do it multi-threaded, you can multitask it yourself (particularly if you expect to run on really old hardware).

The trick is to structure your sort algorithm so that it can be called as part of a multitasking solution. This involves three things:
1. you need to write yourself an event loop
2. you need to watch the clock
3. you need to keep state between calls to your sort and read input functions

The simplest variation (and what AD was probably thinking of) would be to just check _kbhit() every now and then while sorting, and if true, store the current state of the sorting and return to the caller to get the user's input, and continue sorting after dealing with it.

Good luck.

If you do need to port someday, it is trivial to implement it in terms of the new platform.

yeah, on second thought, I guess porting _kbhit() from MSVC/Win32 to a GCC/POSIX solution is not terribly difficult.

So... Dragon's suggestion for using _kbhit() is entirely appropriate.

.

But for whole libraries (particularly conio.h) remember jephthah's words of wisdom.

If you think your program will do very much non-C-standard stuff with the console you should look at NCurses (*nix, OS X) or PDCurses (DOS, Win, OS/2, X11, SDL, etc.).

Actually, I asked one of my teachers today and he did recommend ncurses :) I'm finding it a bit harder to use than expected but I guess I need to find a better tutorial... Thanks for all the tips everyone!

There are only a couple that I'd recommend.

First, straight from esr himself:
http://web.cs.mun.ca/~rod/ncurses/ncurses.html

And another very good one, replete with great examples:
http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/

PDCurses is really a port of NCurses for non-POSIX platforms like Windows and the Amiga and the like. There are a few differences but those generally stem from really unportable stuff where one or the other will explicitly say YRMV.

Have fun! (And be sure to post if you get lost. A few of us here know our way around ncurses pretty well.)

commented: nice links to curses lib and tutorials :) +29

I seem to be having an odd behavior on "closing" windows... Since I'm only really using ncurses to print and sort my list, I'm trying not to rely on it too much. It's possible to do isn't it?

Here's an example:

void print_list(node *header) //called by main, which doesn't use ncurses but has a small menu to load/save files, etc{
	initscr();
	echo();
	nonl();
	cbreak();
//menu stuff
		char input=getch();
		clear();
		//printf("\033[2J"); //LINUX ONLY
		switch (input){
			case '0' :
				continue; // show more items (I only show 9 at the time so the user can select them by pressing 1-9)
			case 'q' :{
				endwin(); //quit list, return to main
				return;
			}
			default: {
				if (input<=i+'0') { //random crap about selecting items
					endwin();
					edit_node(header,selected->rest);
					break;
				}
				else {
					printw("That's not a valid option\n");
				}
			}	
		}
	}
	return;
}

If I edit something (press keys 1-9) I get to see what was up just before I called print_list but edit_node isn't summoned and the program seems to "freeze", I can't interact with main's menus.

Quitting, as in return to main and endwin() (pressing 'q') has the same exact result.

Frankly, you've confused me a little about how you are using curses. I don't know what is causing your error, but typically a curses program will work something like:

int main()
  {
  initialize_curses_my_way();
  while (!done())
    do_stuff();
  endwin();
  return 0;
  }

If you do call endwin(), you only need to call [[B]w[/B]]refresh() to return to curses mode. I'm not sure you ever need to quit it though...

If that doesn't help, can you post a complete code snippit that I can compile and see misbehaving?

I've been trying to do as explained here: http://web.cs.mun.ca/~rod/ncurses/ncurses.html#leaving

It just seems to fail upon endwin(). After calling it, I can see everything I had right before initscr(), the program just seems to sit there doing nothing afterwards. As if it was doing something like

for (;;)
	getchar();

Before adding ncurses it would print a series of options and repeat until I chose something. Upon return it would reprint the options and do it all over. Now it just shows me the echo from my last choice.

I would send you my code but honestly it's way too complex and large. The gui is no where near done and it's all written in Portuguese (my native language). You'd also to open a special database (or create everything manually and save it to disk). It's too much of a hassle, believe me. I'm grateful enough for all the help already :)

PS:
Maybe I'm misunderstanding something: Is it impossible to go back after starting ncurses and use printf, for instance, as usual?

Sorry about double posting but I feel this deserves it's own post.

My idea here is to use ncurses as a simple extension to regular printf'ing.

I once did a python script which only used wx(i think, the details escape me) to allow the user to locate a file on his HDD instead of typing the path. Is it possible to do something similar using curses? Something which resembles:

  • call main
  • call funtion x
  • use ncurses
  • kill ncurses
  • return to main and everything works as it did before starting ncurses

Without a working (or failing, as it were) example I don't know what to say.

If you want to use the standard I/O again you need to do as explained:

endwin();  // leave curses mode
printf( "%s\n", "Hello world! );
sleep( 2000 );
refresh();  // return to curses mode

The curses library is pretty well tested and standardized, and by no less than esr himself (who is a pretty strict coder --he's famous for good reasons), so I suspect it is something you are doing either strange with curses or something else is failing.

[edit]
Sorry, accidentally hit Post when I meant to get back into the edit window...

Also, from your last post... yes, you can do that. Either way works.

Sorry I can't be of more help without more info.
You should see who you can get to take a look over your code. A second pair of eyeballs often solves problems.

#include <stdio.h>
#include <ncurses.h>

void print_stuff() {
	int i;
	for (i=0;i<10;i++)
		printf("%d ",i);
}

void do_stuff(){
	int choice = 0;

	initscr();
	clear();
	noecho();
	cbreak();	/* Line buffering disabled. pass on everything */
	
	printw("Something\n");

	choice=getch();
	if (choice=='q'){
		endwin();
		print_stuff();
		return;
	}
	
	else {
		endwin();
		return;
	}
}

int main() {
	do_stuff();
	if (getchar()=='s')
		printf("You quit");
	return 0;
}

Can someone try and compile this and see if it works as intended, please? I get very odd behavior...

It works fine for me.

BTW, you should #include <curses.h>, not <ncurses.h>, unless you have special needs on a Solaris system.

I compiled using the GCC on Windows XP and PDCurses: gcc a.cpp -lpdcurses and I tried both cases of the line 21 branch.

What OS and hardware are you using?

I'm using Ubuntu 32 bit. Can you see the "You quit" Message? Upon compiling my program runs like this:

See "Something"
press q and see absolutely nothing on the terminal
press any key and enter to see "0 1 2 3 4 5 6 7 8 9"
or, if you pressed 's' and hit enter you'll see "0 1 2 3 4 5 6 7 8 9" and "You quit"

Yep, it works perfectly for me.

Give me a little time to switch over to my Kubuntu partition and try it there.

[edit]
Before I get that far though, if your problem is just that you aren't seeing updates right away it is because you haven't refreshed.

After line 18 should be a refresh() Hope this helps.

Yep, it works perfectly for me.

Give me a little time to switch over to my Kubuntu partition and try it there.

[edit]
Before I get that far though, if your problem is just that you aren't seeing updates right away it is because you haven't refreshed.

After line 18 should be a refresh() Hope this helps.

Thanks, your tip gave me the idea on how to fix it.

The problem is that for some reason the program got to line 35 BEFORE having enough time to print the terminal. A simple fflush(stdout) fixed it

Dammit, my main project still won't work for some reason...

I think I'm getting close though. It's somehow related to my own version of getchar...

do {
		printf("1. Nome completo do restaurante: %s\n",target->dados.nome);
		printf("2. Endereço:  %s\n",target->dados.morada);
		printf("3. Posição:  %f,%f\n",target->dados.x_pos,target->dados.y_pos);
		printf("4. Telefone: %s\n",target->dados.telefone);
		printf("5. Email: %s\n",target->dados.email);
		printf("6. Tipo de comida: %s\n",target->dados.tipodecomida);
		printf("7. Dias de descanso semanal: %s\n",target->dados.diadedescansosemanal);
		printf("8. ");
		print_vacations(&target->dados.periododeferias);
		printf("\n");
		printf("9. Observações: %s\n",target->dados.obs);
		printf("1-9: Editar campo; q: Voltar\n");
		
		c=get_char();
		 
		switch(c) {
			case 'd':
			case 'D': {
				delete_node(header,target);
				printf("Apagado");
				return;
			}
			default: continue;
		}
	}
	while (c!='q' && c!='Q');

If I endwin(), when I reach this part of the program I can't see anything at all. If I keep hiting enter I'll eventualy see the printfs come out like this:

1. Nome completo do restaurante: Andre
2. Endereço:  

3. Posição:  2.000000,3.000000
4. Telefone: 

5. Email: 

6. Tipo de comida: 

7. Dias de

Yes, the 7th line got cut in half. If I press enter 4-5 more times i get to see the rest of it. As soon as I hit 'q' it quits, no matter if I got the whole list or not.

Sorry, haven't rebooted into Linux yet (I will before the night is through).

The way you are mixing standard I/O and curses is not very stable. In each case you must be certain to completely flush your output before doing anything else. And even then it is not guaranteed to behave perfectly on all combinations of hardware and terminfo definitions.

All things considered, why don't you just use curses for all your console I/O? Why keep switching back and forth?

If it makes a difference, you can wrap existing calls to printf() in a variadic macro. Just make sure it is included after <stdio.h>:

// cursed_printf.h

#ifndef CURSED_PRINTF_H
#define CURSED_PRINTF_H

#define printf( fmt, ... ) printw( fmt, __VA_ARGS__ )

#endif

// end cursed_printf.h

Its a kludge, to be sure, but if that's the thing holding you back...

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.