I'm using Lubuntu with LXterminal.
I have (somewhat) unashamedly copied the basis for this code from a stack overflow answer that gives details on c non-blocking keyboard input.
This is the first part:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>
using namespace std;
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
int getch()
{
int r;
unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
Here is one main function that shows some strange behavior.
int main(int argc, char *argv[])
{
unsigned int stor;
set_conio_terminal_mode();
for(int i = 0; i < 6; i++){
while (!kbhit()) {} /* wait */
stor = getch(); /* consume the character */
reset_terminal_mode();
printf("\033[38;33m%i \033[38;0m", stor);
set_conio_terminal_mode();
}
printf("more text\n");
}
What this main loop does is it gets 6 character blocks (ex. ENTER 6 times or arrow key twice.) However, where it says printf there is no printout until the program finishes.
This can be seen better when you add
while(1){}
to the end of the main function.
So what's going on here? Is there some sort of magic that happens at the end of the program that releases all of the printf functions?
How do I make it printf when the program is still running?
Apparently, you are victim of excessive buffering.
Try to disable buffering using setvbuf.
To completely disable buffering on stdout:
setvbuf(stdout, (char *)NULL, _IONBF, 0);
To enable buffering for each line:
setvbuf(stdout, (char *)NULL, _IOLBF, 0);
// or
setlinebuf(stdout);
Related
I wrote the following code to implement a periodic thread terminated when someone presses escape. The result is a periodic thread that continues also after i pressed escape.
Can you tell me where is the error please? The compiler compiles without any error and warning. Before reading the code jump to EDIT below.
#include <stdio.h>
#include <pthread.h>
#include <allegro.h>
#include <time.h>
void *task(void *p);
void time_add_ms(struct timespec *t, int ms);
int main()
{
int tret;
int a = 1;
allegro_init();
install_keyboard();
tret = pthread_create(&tid, NULL, task, (void*)&a);
pthread_join(tid, NULL);
printf("Thread1 returns %d\n", tret);
allegro_exit();
return 0;
}
/* Sommare quantità temporale espressa in ms al tempo nella struttura timespec */
void time_add_ms(struct timespec *t, int ms)
{
t->tv_sec += ms / 1000;
t->tv_nsec += (ms % 1000) * 1000000;
if (t->tv_nsec > 1000000000){
t->tv_nsec -= 1000000000;
t->tv_sec += 1;
}
}
/* Funzione da trasformare in task */
void *task(void *p)
{
struct timespec t;
int period = 100;
int *pi;
char scan = 0;
clock_gettime(CLOCK_MONOTONIC, &t);
time_add_ms(&t, period);
pi = (int *)p;
while (scan != KEY_ESC) {
if (keypressed()) scan = readkey() >> 8;
printf("This is TASK %d\n", *pi);
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t, NULL);
time_add_ms(&t, period);
}
}
EDIT: maybe i found the problem, when i press escape, it prints the symbol ^, so the compiler or allegro doen't recognize the esc key.
This is not exactly finding the error, but I think it is what you want (more details here)
If you add following code to your main(), this will disable canonical mode, which will basically enable you to get characters immediately without waiting for a '\n' or EOF (or enter keypress in short) (already explained in the link above):
#include <termios.h>
#include <unistd.h>
int main(){
static struct termios oldt, newt;
tcgetattr( STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON);
tcsetattr( STDIN_FILENO, TCSANOW, &newt);
// rest of your code
}
And the thread loop here (I've changed it a little bit)
while (1) {
printf("This is TASK %d\n", *pi);
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t, NULL);
time_add_ms(&t, period);
if(getchar()==27) break;
}
I have a custom _kbhit() function used for non-blocking character input.
_kbhit() function uses select() to check if stdin is ready to read.
But finally I have stdin ready if only I pressed RETURN.
How to make it work when I press any key?
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &new_termios);
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int _kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
static char _getch(void) {
int r;
unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
int main( int argc, char *argv[] )
{
set_conio_terminal_mode();
while (1) {
if (_kbhit()) {
inputbuffer[inputlen] = _getch();
if (inputbuffer[inputlen] >= 0x20 && inputbuffer[inputlen] < 0x7F) {
putchar(inputbuffer[inputlen]);
inputlen++;
}
}
}
}
I expected that this code will output echo, but it outputs entire string just after I press RETURN.
I think the problem is that - despite your initialization of the terminal attributes using cfmakeraw() - stdout is still in line-buffered mode.
Try setting standard output to unbuffered mode.
setbuf(stdout, NULL);
which is equivalent to
setvbuf(stdout, NULL, _IONBF, 0);
This avoids the need to call fflush(stdout); after every putchar(inputbuffer[inputlen]);.
There may be a way of accomplishing this using tcsetattr() instead of setvbuf(), buf I am not familiar with it.
We are testing serial port communication. There are two tty nodes /dev/ttyUSB8 and /dev/ttyUSB9 on the device.
When I transmit buffer from /dev/ttyUSB8 to /dev/ttyUSB9 I don't receive data on the /dev/ttyUSB9 read call if the buffer doesn't contain new line.
Transmit Code
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
void write_func()
{
int fdw;
int i;
fdw = open("/dev/ttyUSB8", O_RDWR);
printf("fdw : %d\n",fdw);
printf("%ld\n", write(fdw, "Hello", 6));
close(fdw);
}
int main()
{
int i;
write_func();
return 0;
}
Receive Code
void read_thread()
{
int fdr;
char buf[] = "NoData";
fdr = open("/dev/ttyUSB9", O_RDWR);
printf("fdr : %d\n",fdr);
printf("%s: %ld\n", __func__, read(fdr, buf, 6));
printf("%s\n", buf);
close(fdr);
}
int main()
{
int i;
read_thread();
return 0;
}
I don't receive data with the above call, but when I add '\n in the write call i get data in the read block call.
printf("%ld\n", write(fdw, "Hello\n", 7));
What is the significance of new line character in this..
Update:
I added the code to reset canonical mode, still it didn't work:
void write_thread()
{
int fdw;
int i;
struct termios config;
fdw = open("/dev/ttymib24", O_RDWR);
printf("fdw : %d\n",fdw);
tcgetattr(fdw, &config);
config.c_lflag &= ~ICANON;
tcsetattr(fdw, TCSANOW, &config);
printf("%ld\n", write(fdw, "Hello", 6));
close(fdw);
}
Your tty is probably in canonical mode.
Try to reset ICANON by using tcsetattr(). Something like this:
struct termios termiosv;
tcgetattr(fd, &termiosv);
termiosv.c_lflag &= ~ICANON;
tcsetattr(fd, TCSANOW, &termiosv);
More information in man page of termios:
In canonical mode:
* Input is made available line by line. An input line is available
when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at
the start of line). Except in the case of EOF, the line delimiter
is included in the buffer returned by read(2).
I want to read one character at a time from standard input and operate on that. For example, input
abcdefghijklmnopqrstuvwxyz
What I want is, to operate on a (which is the first character) as soon as it has been entered (the operation on a should be done before the user enters b) and then operate on b and so on.
Maybe this other solution.
Taken from https://www.gnu.org/software/libc/manual/html_node/Noncanon-Example.html and https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_chapter/libc_17.html.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
/* Use this variable to remember original terminal attributes. */
struct termios saved_attributes;
void
reset_input_mode (void)
{
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}
void
set_input_mode (void)
{
struct termios tattr;
char *name;
/* Make sure stdin is a terminal. */
if (!isatty (STDIN_FILENO))
{
fprintf (stderr, "Not a terminal.\n");
exit (EXIT_FAILURE);
}
/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);
/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
tattr.c_cc[VMIN] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}
int
main (void)
{
char c;
set_input_mode ();
while (1)
{
read (STDIN_FILENO, &c, 1);
if (c == '\004') /* C-d */
break;
else
putchar (c);
}
return EXIT_SUCCESS;
}
I think you want something like this.
#include <stdio.h>
int main ()
{
int c;
puts ("Enter text");
do {
c = getchar();
putchar (c); //do whatever you want with this character.
} while (c != '\0');
return 0;
}
Since you did not specify an operating system, I am going to give a suggestion suitable for the windows operating system.
The function GetAsyncKeyState() does exactly what you ask for. You can read its documentation from this link.
As a quick example on its usage:
#include <Windows.h>
int main(void)
{
while(1) {
if(GetAsyncKeyState('A') & 0x8000) {
/* code goes here */
break;
}
}
return 0;
}
I have a very small C program which sends and receives newline-terminated ASCII strings to and from a serial device. It's plugged into my computer with a USB adapter, on /dev/ttyUSB0.
Most of the time it sends the commands just find, but occasionally it will capitalize all the lower-case letters to upper-case. It leaves all special characters alone.
The string I am sending is /home\n. About 1 out of every five times I run the program (by simply running ./a.out without recompiling), the sent message understood by the device is /HOME\n.
Here is my source code:
#include <stdio.h>
#include <stdlib.h>
#include "zserial.h"
int main() {
char buf[256];
int fd = connect("/dev/ttyUSB0");
char *cmd = "/home\n";
send(fd, cmd);
receive(fd, buf, 256);
puts(buf);
exit(0);
}
And zserial.c:
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zserial.h"
int send(int fd, char *buf) {
int len = strlen(buf);
int nb = write(fd, buf, len);
if (len != nb || nb < 1)
perror("Error: wrote no bytes!");
tcdrain(fd);
return nb;
}
int receive(int fd, char *dst, int nbytes) {
int i;
char c;
for(i = 0; i < nbytes;) {
int r = read(fd, &c, 1);
/* printf("Read %d bytes\n", r); */
if (r > 0) {
dst[i++] = c;
if (c == '\n') break;
}
}
dst[i] = 0; /* null-terminate the string */
return i;
}
int connect(char *portname) {
int fd;
struct termios tio;
fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
tio.c_cflag = CS8|CREAD|CLOCAL;
if ((cfsetospeed(&tio, B115200) & cfsetispeed(&tio, B115200)) < 0) {
perror("invalid baud rate");
exit(-1);
}
tcsetattr(fd, TCSANOW, &tio);
return fd;
}
What am I doing wrong? Is there some termios flag which modifies the output on a serial port?
c_oflag & OLCUC turns on the mapping of lowercase to uppercase on output. Since you never initialized tio, it's not surprising you got some random flags set.
You have two choices:
tcgetattr the current settings into a termios struct to initialize it, then modify the ones you're interested in, then write them back with tcsetattr
initialize all the termios fields to known values, not just c_cflag and the speed fields.