3

I just made a little chat CLI-application that works pretty fine actually!

Unfortunately, when one types a message and receives other's messages at the same time, both interlacing... Which renders quite awful:

elton: Hey!
john: how are you doing?
fine anjohn: still not to bed?!
d john: haha
you?elton: fine and you?

I'm looking for a way to avoid this kind of problem. Such as "reserving" the last line for user-input or process some actions when data are received to recalculate the position of user-input.

After some research I found that I shall retrieve each character one by one with getch(). So that I can check regularly if a new message is waiting to be displayed and handle this case.

But if I use getch() I have to redefine manually basic actions (such as backspace, move left and right...), some characters take more than one byte. In brief, it's unusable.

Antoine Pinsard
  • 33,148
  • 8
  • 67
  • 87

2 Answers2

1

The usual way to do this kind of thing is by writing a "console-mode GUI" instead of a pure CLI.

For example, you can very easily create a very simple interface with a ROWS-2xCOLS output window, a 1xCOLS divider or mode line, and a 1xCOLS input window—as seen in things like IRC clients, emacs, etc. For example:

Or you could do a split view, with a ROWS/2xCOLS window for the other person/people's chat, and a ROWS/2xCOLS window below that for the user's current and previous messages, as seen in most of the early Unix chat programs.

Or something fancy with windows all over the place that pop up and go away as appropriate, as some modern IRC clients do.

Python has curses support built-in—but not on Windows. If you want a cross-platform solution, or a Windows-only solution, see curses-like library from cross-platform console app in python, and the answer is probably there.

Here's a screenshot from the IRC client BitchX, to give you an idea of what's possible without too much work: BitchX

Community
  • 1
  • 1
abarnert
  • 354,177
  • 51
  • 601
  • 671
1

As far as I can tell, you have a few options here. Normally just calling getch you're running the terminal in what's called "cooked" mode. This means that the terminal just displays characters in the order they arrive, nothing too special. However you run in to race conditions as you've discovered.

Option 1: you read things in one character at a time, buffering the entire line. When a new line arrives and you want to print it out, you'd have to (a) grab hold of some kind of print mutex (internal to the program), (b) clear out the current line (print '\r[space][space][space]...\r'), (c) print the incoming line + '\n', and (d) restore the previous buffer to the screen (via print) and unlock the mutex.

This gets ugly. Fast. As you discovered, there's no line editing support or anything fancy. The only good thing about this approach is that it'll probably work in about 99% of terminals.

Option 2: you put the terminal in to raw mode. In this mode, you have complete control over the terminal, but you lose very basic functionality (e.g. typing a key won't display it unless you manually output it). You would still have to manually implement things like navigation, but at least it would work. This method is probably the most flexible and give you the most control, however it's very difficult to work like this, which is why they invented...

Option 3: you use ncurses (or a similar terminal library). The concepts are a little difficult, but FAR easier to learn than doing things in pure raw mode. Editing and windowing (or as you put it, reserving a line) are built-in. The interface can be much prettier than you'd get from cooked mode.

EDIT I'm talking a lot about *nix terminals here, most of which doesn't apply for Windows.

Chris Eberle
  • 47,994
  • 12
  • 82
  • 119
  • Options 2 and 3 are both Unix-specific, as is the whole discussion of cooked vs. raw mode. But I suspect the OP is on Windows. (There's an `msvcrt.getch` in the stdlib, but nothing equivalent for Unix out of the box.) – abarnert Jan 16 '13 at 21:23
  • @abarnert I'm on GNU/Linux but I found a patch for getch on Unix. Anyway, I must get it to work on both Windows and Unix. So I will use the curses solution. – Antoine Pinsard Jan 16 '13 at 21:32
  • @abarnert you're absolutely correct. I did make that assumption. I'll edit this to reflect that. – Chris Eberle Jan 16 '13 at 21:39