Created 2022-10-20 Updated 2053-02-12
Raw, unblocked, stdin
/*
* 2023-03-31 Started
*/
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdexcept>
#include <termios.h>
static struct termios term_ori; // original term that we want to restore when we've finished
void term_init(void)
{
// https://linux.die.net/man/3/termios
struct termios term;
if(tcgetattr(STDIN_FILENO, &term) < 0) exit(1); // what are the current attributes?
term_ori = term; // creat a copy for later restoration
cfmakeraw(&term); // raw mode; char-by-char, echo disabled, special chars disabled
// cfmakeraw() does too much, so we have to set things back again
term.c_iflag |= ICRNL; // translate CR to NL on input
term.c_oflag |= OPOST; // get newlines working properly again on output
term.c_lflag |= ISIG; // re-enable INTR, QUIT, SUSP, DSUPS. Ctl-C will work again.
if(tcsetattr(STDIN_FILENO, TCSANOW, &term)) exit(1); // set the attributes how we wish
}
void term_deinit(void)
{
if(tcsetattr(STDIN_FILENO, TCSANOW, &term_ori)) exit(1);
}
/*
* NB stdout will be supressed unless there is a \n for an fflush(stdout)
*/
int get_key_non_blocking(void)
{
int fd;
char c;
const nfds_t nfds = 1;
struct pollfd pfds[nfds];
int timeout = 1; // ms
const int stdin_fd = STDIN_FILENO;
pfds[0].fd = stdin_fd; // stdin
pfds[0].events = POLLIN | POLLHUP;
//while(1) {
int n = poll(pfds, nfds, timeout);
if(n==0) return -1;
if(pfds[0].revents & POLLHUP) {
return EOF;
}
if(pfds[0].revents & POLLIN) {
int i = read(stdin_fd, &c, 1);
return c;
//putchar(toupper(c));
//printf(" %d, ", c);
//fflush(stdout);
}
//}
return -1;
}
int main()
{
term_init();
while(1) {
char c = get_key_non_blocking();
if(c == -1) continue;
if(c == 'x') break;
putchar(c);
fflush(stdout);
usleep(1000);
}
term_deinit();
return 0;
}
Raw, blocked, stdin
This program demonstrates the use of
See also: man tty_ioctl
It stops character echoing on the terminal. Newlines are still echoed properly. Any character that the user inputs is printed out in uppercase. `poll()` is used to check to see if a char is available, rather than using `getchar()`.
/* test polling of input of stdin and writes the input in upper case.
*
* Only need to do this prior to 2022-10-21:
* --- Begin ---
* Run:
* stty -echo -icanon time 0 min 0 && ./poll
* Afterwards, return tty to sane state:
* stty sane
* --- End ---
*
* 2022-12-02 Added some modularity
* 2022-10-21 Controlling how input and output works via termios
* 2022-07-21 created. Works
*/
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdexcept>
#include <termios.h>
static struct termios term_ori; // original term that we want to restore when we've finished
void term_init(void)
{
// https://linux.die.net/man/3/termios
struct termios term;
if(tcgetattr(STDIN_FILENO, &term) < 0) exit(1); // what are the current attributes?
term_ori = term; // creat a copy for later restoration
cfmakeraw(&term); // raw mode; char-by-char, echo disabled, special chars disabled
// cfmakeraw() does too much, so we have to set things back again
term.c_iflag |= ICRNL; // translate CR to NL on input
term.c_oflag |= OPOST; // get newlines working properly again on output
term.c_lflag |= ISIG; // re-enable INTR, QUIT, SUSP, DSUPS. Ctl-C will work again.
if(tcsetattr(STDIN_FILENO, TCSANOW, &term)) exit(1); // set the attributes how we wish
}
void term_deinit(void)
{
if(tcsetattr(STDIN_FILENO, TCSANOW, &term_ori)) exit(1);
}
/*
* NB stdout will be supressed unless there is a \n for an fflush(stdout)
*/
int get_key_blocking(void)
{
int fd;
char c;
const nfds_t nfds = 1;
struct pollfd pfds[nfds];
int timeout = 1; // ms
const int stdin_fd = STDIN_FILENO;
pfds[0].fd = stdin_fd; // stdin
pfds[0].events = POLLIN | POLLHUP;
while(1) {
int n = poll(pfds, nfds, timeout);
if(n==0) continue;
if(pfds[0].revents & POLLHUP) {
return EOF;
}
if(pfds[0].revents & POLLIN) {
int i = read(stdin_fd, &c, 1);
return c;
//putchar(toupper(c));
//printf(" %d, ", c);
//fflush(stdout);
}
}
}
int main()
{
term_init();
puts("type 'q' to quit. I will echo output in uppercase");
while(1) {
//putchar('.');
int c= get_key_blocking();
if(c == EOF) {
printf("\nEOF detected\n");
break;
}
if(c == 'q') break;
putchar(toupper(c));
fflush(stdout); // important!
}
term_deinit();
puts("Bye now");
return 0;
}
Source
Raw, blocked, tty
This example takes input from the tty in non-coocked mode, so it doesn't require the user to press Return before responding. The following example converts your input key to uppercase. It blocks on input, and echos the input key to output. You can, optionally, use stdin from file redirection (as per note 1).
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
int main(void)
{
struct termios term, term_ori;
int fd = open("/dev/tty", O_RDONLY);
if(tcgetattr(fd, &term) < 0) exit(1); // what are the current attributes?
term_ori = term; // create a copy for later restoration
term.c_lflag &= ~ICANON; // don't wait for newline
if(tcsetattr(fd, TCSANOW, &term)) exit(1); // set the attributes how we wish
// see note 1
int c;
while(n = read(fd, &c, 1)) {
if(c == 'q') break;
c = toupper(c);
write(STDOUT_FILENO, &c, 1);
}
close(fd);
if(tcsetattr(STDIN_FILENO, TCSANOW, &term_ori)) exit(1);
return 0;
}
Note 1
You can also additionally use stdin:
int n;
char buf[101];
while(n = fread(buf, 1, 100, stdin)) {
if(n==0) break;
buf[n] = 0;
printf("%s", buf);
}
puts("OK, I've finished echoing stdin, now type something. q to quit");
So you can do:
and it will print the file to stdout, and accept input from the tty.
Keycode recognition
The following code shows raw key codes for terminal input keys:
/*
* show raw key codes of terminal keys input
*
* 2022-12-05 Started. Works
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <stdlib.h>
int main()
{
struct termios term, term_ori;
int fd = open("/dev/tty", O_RDONLY);
if(tcgetattr(fd, &term) < 0) exit(1); // what are the current attributes?
term_ori = term; // create a copy for later restoration
term.c_lflag &= ~ICANON; // don't wait for newline
if(tcsetattr(fd, TCSANOW, &term)) exit(1); // set the attributes how we wish
char buf[6];
int n;
while(n = read(fd, &buf, sizeof(buf))) {
printf("\t");
for(int i = 0; i< sizeof(buf); i++) {
printf(" %3d", buf[i]);
}
puts("");
if(buf[0] == 'q') break;
}
close(fd);
if(tcsetattr(STDIN_FILENO, TCSANOW, &term_ori)) exit(1);
return 0;
}
Example output:
This is the result of pressing the Page Down Key. ESC is decimal 27 (\033), 91 is '[', 54 is '6', and 126 is '~'.
Source code
SERIAL PORT
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <stdlib.h>
int main()
{
struct termios tio0, tio;
const char device[] = "/dev/ttyACM0";
int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
if(fd == -1) {
printf( "failed to open port\n" );
}
tcgetattr(fd, &tio0);
tio = tio0;
tio.c_lflag &= ~ICANON; // disable canonical mode
cfsetispeed(&tio, B115200);
cfsetospeed(&tio, B115200);
tcsetattr(fd, TCSANOW, &tio);
for(int i = 0 ; i < 10; i++) {
char c = 1;
write(fd, &c, 1);
usleep(500'000);
}
close(fd);
}
Source
ANSI
0: Reset style
1: Bold
2: light
3: italics
4: underlined
30: Black
31: Red
32: Green
33: Yellow
34: Blue
35: Magenta
36: Cyan
37: White
Usage: echo -e "\033[31mI am red"
Combining multiple:
Use ';' to separate elements
Use 'm' to finish sequence
E.g.: \033[31;1m will be bold red
\033[A up arrow
\033[B down arrow
\033[C right arrow
\033[D left arrow
\033[F end
\033[H home
\033[J clear screen
\033[2~ insert
\033[3~ delete
\033[5~ page up
\033[6~ page down
Cursor
printf("\033[?25l"); // cursor off
printf("\033[?25h"); // cursor on
EXITS
termios(3) - terminal interface functions
cerbo/cprogs for terminal size