To build a chat, I have to send and receive messages.
Currently, I am sending and receiving messages from the command line.
When I receive a message while I was typing one, the received message is printed and I can't see what was typed before. I use the read()command to catch the user input.
Here is an example:
Let's suppose I want to send the message Hello World and that I receive a message after typing Hello Wor.
This is what happens :
$ Enter your message : Hello Wor
$ Somebody: This is a message
$ Enter your message :
But Hello Wor is still on the command line: if I press the Delete Key, I will see Hello Wo.
This is what I want :
$ Enter your message : Hello Wor
$ Somebody: This is a message
$ Enter your message : Hello Wor
I would like to save what was typed before printing the received message and print it.
Without using a terminal control API like curses or ncurses, you will need to change your program to work like-so:
Disable local echo, so your program handles every local keypress before it's rendered by the caret in the terminal.
Your local keypress handling code will need to be asynchronous so it doesn't block on getchar while waiting for the user to enter the next character of their message or [Enter].
This is important so that your program can still run to collect and display messages received on the network end. While you could use a second thread for this it would mean two threads would be using stdio at the same time which is inadvisable, while you could use thread synchronization primitives to prevent this bug you're better-off using an asynchronous IO API instead.
Important note: "asynchronous" code does not mean "concurrent" nor does it imply the use of threading. In practice, it means the program's thread will return to a central dispatcher (cooperative yielding) instead of blocking on IO and the dispatcher will resume the function where it yielded when the IO operation completes (this is what the await keyword does in C# and JavaScript).
However, in C, working with the asynchronous terminal/console API in Linux is a PITA and you're better off using a library like ncurses which handles this for you.
When your program receives a message to display, you would clear the bottom line of the window (by overwriting it with whitespace or using VT100 ^[[2K), write the received message, then re-write the user's incomplete entered input (if any) on a new line.
Repeat.
Related
i am writing a terminal chat and would like to reprint the content the user has typed in, if a new message from another user has arrived asynchronously.
if a new message arrives i print "\x1B[2K" to stdout (ANSI ESCAPE CODE for erasing the current line) to clear the current line, then i print "\r" to move the cursor to the leftmost position, then i print the received message with a newline
now i would like to reprint the characters the user has typed - i found out that there is a special character VREPRINT (http://www.gnu.org/software/libc/manual/html_node/Editing-Characters.html) that can be used and if a hit CTRL-R it actually works... but if print the character to stdout using the character placed in c_cc[VREPRINT] of a struct termios, it does not work - is it even possible to do it that way?
I do not want to use any other libraries like readline or ncurses, since this would be plain overkill... i just would like to make my solution work using ICANON terminal mode if possible
Thanks in advance!
While it is possible to write to the same tty device that you are reading from, this doesn't mean that the characters that appear will be processed as input characters.
You can test this yourself, using two separate shell sessions (using screen, tmux, or, if you are on MacOSX, iTERM.app sessions).
in Session 1, get the current tty name:
tty
Let's assume the tty name of Session 1 is /dev/ttys000.
Then enter the following, without a return or newline -- just leave it pending:
echo abc def-
In Session 2 (using the tty name from Session 1), enter the following command:
echo foo >/dev/ttys000
The string foo should appear on the pending line in Session 1, like this:
echo abc def-foo
Now, go back to Session 1, and hit an "enter" (or return), causing the input buffer to be completed and sent to the shell, which will parse the first word as the echo command, with the subsequent arguments as text to be printed.
You should see the string "abc def-" echoed -- but not the foo.
This test makes it clear that the string foo was never placed into the input buffer, even though it was sent to the same tty being used for input (and output).
You can try to see if special control characters are recognized and processed; but they won't be. These characters are only being output to the terminal, but not processed.
In order for the special characters to be processed, they have to be received via the socket connected to the input device.
To accomplish what you are trying to do, you'll have to go non-canonical (stty -icanon) and process every character in your code, in order to collect the pending input and also be able to produce asynchronous output.
Alternatively, using nurses is not hard at all, and if you are writing a terminal-based chat program, I would suggest creating at least two panels: a "chat" panel for all output, and an "input" panel for user input. This allows output from other users, as well as the completed commands from the "input" panel, to be received and written to the "chat" panel asynchronously without disturbing the current command in progress by the user in the "input" panel.
For my situation, say Tim and Bob are chatting using my C chat client and server. My chat client and server is executed on a bash terminal. The cursor for this chat program is the ~ key.
Tim sends Bob a message that says, "Hey". Now say that Bob receives this message as he's typing a message to Tim. Bob wants to send "Hello" to Tim, but he hasn't finished writing it yet (so he's only written "Hel" so far).
I would like my application to be able to keep Bob's prompt and his entry so far at the bottom of the terminal, and display Tim's message above the prompt. Bob should still be able to finish his message to Tim, as well as being able to see Tim's message. Below is a diagram of what I mean, from the view of Bob's client. Is there any way of accomplishing something like this using C?
........................Before............................................................................................After...............................
............................... .................................
............................... <Tim> Hey
Enter Message> Hel~ Enter Message> Hel~
If you know that you're using an ANSI compatible terminal AND know your prompt length AND cursor position (assuming you allow editing of input so that the cursor may not be at the end of the current input) AND don't allow more than one line of input [ a lot of assumptions there ] then it would seem you can output one or more 'cursor up' sequences then a carriage return, your message from Tim, a carriage return, a linefeed and then lots of 'cursor right' sequences to put the cursor back where it was.
Basically, though, if you want to handle this sort of complication then you're going to head to a curses/ncurses/terminfo kind of library.
My embedded system has one background program which generates some output message to console.
When it's not finished, the login program starts and prompt login string to same console as well.
My question is, when the login prompt string comes out, the output message generated by the background program is not aligned like this:
Embedded System login: msg_line1...
msg_line2...
msg_line3...
The expected output should be:
Embedded System login: msg_line1...
msg_line2...
msg_line3...
msg_line4...
Have no idea how to resolve the problem...
Could anyone help?
THX!!
Configure your terminal program to autolinefeed, so that it generates the \r internally whenever it receives a \n.
use a redirection for your background program
YourBackStuff.sh >/tmp/back_out.1 2>/tmp/Back_out.2 &
or refresh the screen.
Notice that only the appearance is modified, the real output ou current application/shell (so not the background one) is correct, only the terminal show your all the info received. So
YourFrontApp.sh | tee /tmp/front_out.1
cat /tmp/front_out.1
will produce a correct display (when background process stop)
Looks to me like your messages are sent with a new-line character (represented by \n in C) at the end of each line. This requests a new line from the terminal, which it duly provides. It does not request a carriage return (i.e. "go back to the left hand edge"). This would be represented in C as a \r.
To fix it, you need to set your terminal application to interpret \n as \n\r - it will have a setting for "treat newlines as newline+CR" or some such.
I'm trying to write a chat client and server and the client should be able to print the messeges from the server while also writing something on the console.
So I created a pthread that should read the user input and the main thread prints the messeges from the server. But when I type something and while typing a messege is received, the text I was typing is pushed up on the console. How can i fix that?
for example:
I'm typing "abcdef" and then when I get a messege from the server (but didn't finish typing yet) it will look like this:
abcdef[Chatuser1]:Hello
Use synchronization technique like semaphore or mutex to synchronize input and output in your multi threaded program
I would really recommend doing something like this in a GUI with two seperate edit box elements and not in a console.
If you still want to do it in a console, you will need to do direct console buffer modification.
Every time a new output message arrives, the current input message has to be backed up so you can savely write to the console then do some custom scrolling and add the input message back again. Thread syncronisation is needed to prevent mixing of input and output in the buffer.
I think you also would not get around custom key handling, because otherwise you have no access to partialy typed input.
I'm doing a lab assignment where we make a Server program and a Client program. Its on the QNX OS. Not sure if it runs in Linux. The outline is this:
"Write a pair of C programs msgSender.c and msgLogger.c to demonstrate Neutrino message passing between processes.
Your programs will be called from the shell as:
$ msgLogger logFileName
$ msgSender msgLogger
logFileName is the name of the log that stores the messages
The msgLogger process acts as a logger. It receives messages and writes the messages to a file.
msgLogger receives text-based messages of the format shown in msg.h. It must test the message header and only write message text to the logFile if the message type is MSG_DATA.
If MSG_DATA is received, the reply status is MSG_OK.
If a MSG_END is received, the server replies with a status of MSG_END and then cleans up and exits.
If the received message is not MSG_DATA or MSG_END, the reply status is MSG_INVALID and the message text is not logged. A warning message is logged.
This process advertises its presence by writing its ND PID CHID to a file named msgLogger.pid where the "msgLogger" part of the filename is taken from argv[0].
The logged messages are stamped with the time and the ND PID COID of the sender.
The msgSender is an interactive program that assembles and sends the text-based message.
Reads the name of the logger process from the command line and uses this name to build the name of the .pid file where it reads the ND PID CHID.
It prompts the user for the message header type and then for the text of the message.
It will exit if it receives a MSG_END from the server.
It prints a warning if MSG_INVALID is received from the server
Your client and server must interoperate with my client and server.
Validate that the server works properly with multiple concurrent clients.
If you flush your server's file write buffer after each logging message, you can run it in the background and use
$ tail -f logFile
to view the messages as they are received.
Be sure to check the validity of the command-line argument.
Use global variables only when necessary.
"
I've got the msgLogger fully working; here is the code:
http://pastebin.com/8AGfGZ5u
And here is the msg.h file:
http://pastebin.com/3xcBZvnH
And here is the code I have so far for msgSender:
http://pastebin.com/Buk88Kry
What the sender (client) needs to do, is let the user enter the message type using digits. The msg.h file contains the type of message numbers with MSG_DATA being 1, etc. If they enter an invalid digit, it'll ask them to try again, else it will store that digit and assign it to the amsg.m_hdr of the MESSAGE struct. amsg.m_data is the value with the message.
Then the user enters the message they want, and if they chose the digit 1 (msg_data), the server sends notification and the client prints "message successfully received", while the contents of the message are saved to the log file.
Unfortunately I'm having a bunch of problems and it's not logging the message. I have to hand in the msgSender tomorrow, and it's also dependent on my next lab. I really hope I can get some help on this.
Try flushing the buffer after the client writes. If you don't close the file descriptor while writing to the file, there's no gaurentee that the buffer was flushed and the file was written. You can ensure that all your writes sync to the file whne you want by calling fsync().