💾 Archived View for sdf.org › blippy › terminal-programming.gmi captured on 2023-07-22 at 16:35:55. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-07-10)
-=-=-=-=-=-=-
Created 2022-10-20 Updated 2023-04-03
/* * 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; }
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; }
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; }
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:
a.out < myfile.txt
and it will print the file to stdout, and accept input from the tty.
/* * get the size of the terminal * * see also TIOCSWINSZ for setting terminal size * * 2022-12-04 Started */ #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> void say(char *src, unsigned short val) {printf("%s\t%d\n", src, val); } int main() { int fd = open("/dev/tty", O_RDONLY); struct winsize wz; int ret = ioctl(fd, TIOCGWINSZ, &ws); if(ret==-1) { perror(strerror(errno)); } say("num rows:", ws.ws_row); say("num cols:", ws.ws_col); say("num xpixels:", ws.ws_xpixel); say("num ypixels:", ws.ws_ypixel); close(fd); return 0; }
Typical output:
num rows: 24 num cols: 80 num xpixels: 0 num ypixels: 0
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:
^[[6~ 27 91 54 126 0 0
This is the result of pressing the Page Down Key. ESC is decimal 27 (\033), 91 is '[', 54 is '6', and 126 is '~'.
#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); }
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
printf("\033[?25l"); // cursor off printf("\033[?25h"); // cursor on