flbng

flash bang - show full screen centered text in terminal
git clone git://git.konyahin.xyz/flbng
Log | Files | Refs | README | LICENSE

main.c (3818B)


      1 #include <ctype.h>
      2 #include <err.h>
      3 #include <fcntl.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <sys/ioctl.h>
      8 #include <termios.h>
      9 #include <unistd.h>
     10 
     11 struct screen_size {
     12     unsigned short width;
     13     unsigned short height;
     14 };
     15 
     16 struct data {
     17     char **data;
     18     size_t lines;
     19     size_t max_width;
     20 };
     21 
     22 // global variable for terminal state restoring
     23 struct termios orig_term;
     24 
     25 void
     26 restoreTerminal() {
     27     // show cursor
     28     puts("\e[?25h");
     29     if (tcsetattr(STDOUT_FILENO, TCSAFLUSH, &orig_term) < 0)
     30         err(1, "can't restore terminal state");
     31 }
     32 
     33 void
     34 prepareTerminal() {
     35     if (tcgetattr(STDOUT_FILENO, &orig_term) < 0)
     36         err(1, "can't get terminal attributes");
     37 
     38     atexit(restoreTerminal);
     39 
     40     // havely inspired by https://github.com/antirez/kilo/blob/master/kilo.c
     41     struct termios raw = orig_term;
     42     raw.c_iflag &= 
     43         (unsigned) ~(BRKINT | INPCK | ISTRIP | ICRNL | IXON);
     44     raw.c_oflag &= (unsigned) ~(OPOST);
     45     raw.c_cflag |= (CS8);
     46     raw.c_lflag &= 
     47         (unsigned) ~(ECHO | ICANON);
     48     raw.c_cc[VMIN] = 0;
     49     raw.c_cc[VTIME] = 1;
     50 
     51     if (tcsetattr(STDOUT_FILENO, TCSAFLUSH, &raw) < 0)
     52         err(1, "can't set terminal attributes");
     53 
     54     // hide cursor
     55     puts("\e[?25l");
     56     // clear screen
     57     puts("\x1b[2J");
     58 }
     59 
     60 void
     61 processInput() {
     62     // read from /dev/tty, because standart input
     63     // will be redirected
     64     int input = open("/dev/tty", O_RDONLY);
     65     char c = '\0';
     66     while (1) {
     67         ssize_t read_s = read(input, &c, 1);
     68         if (read_s < 0)
     69             err(1, "can't read from tty");
     70         if (read_s == 0)
     71             continue;
     72         break;
     73     }
     74 }
     75 
     76 struct screen_size
     77 getScreenSize() {
     78     struct winsize size;
     79     ioctl(STDOUT_FILENO, TIOCGWINSZ, &size);
     80     return (struct screen_size) {
     81         .width = size.ws_col,
     82         .height = size.ws_row,
     83     };
     84 }
     85 
     86 struct data
     87 getInput(struct screen_size s) {
     88     struct data text;
     89     text.data = calloc(sizeof(char*), s.height);
     90     text.lines = 0;
     91     text.max_width = 0;
     92 
     93     while (1) {
     94         char *line = NULL;
     95         ssize_t len = 0;
     96         size_t buf_len = 0;
     97 
     98         if ((len = getline(&line, &buf_len, stdin)) < 0)
     99             break;
    100 
    101         // we can reach EOF here, if there is no delimiter on the last line
    102         // if there is delimiter - we should remove it
    103         if (!feof(stdin)) {
    104             len -= 1;
    105             line[len] = '\0';
    106         }
    107 
    108         if ((size_t) len > text.max_width) {
    109             text.max_width = len;
    110             if (len > s.width)
    111                 errx(1, "too long line for this terminal, should be shorter than %d", s.width);
    112         }
    113         text.data[text.lines] = line;
    114         text.lines += 1;
    115         if (text.lines > s.height)
    116             errx(1, "too much lines for this terminal, should be less than %d", s.height);
    117     }
    118 
    119     if (ferror(stdin))
    120         err(1, "can't read line from stdin");
    121 
    122     if (text.lines == 0)
    123         errx(1, "empty input");
    124 
    125     return text;
    126 }
    127 
    128 int
    129 main(void) {
    130     // TODO: -h for help
    131 
    132     struct screen_size s = getScreenSize();
    133     struct data text = getInput(s);
    134 
    135     size_t start_position = (s.height - text.lines) / 2;
    136     int margin = (int) ((s.width - text.max_width) / 2);
    137 
    138     prepareTerminal();
    139 
    140     for (size_t i = 0; i < start_position; i++)
    141         printf("\r\n");
    142 
    143     for (size_t i = 0; i < text.lines; i++) {
    144         if (margin)
    145             printf("%*s", margin, " ");
    146         printf("%s", text.data[i]);
    147         if (i != text.lines - 1)
    148             printf("\r\n");
    149     }
    150 
    151     for (size_t i = start_position + text.lines; i < s.height; i++)
    152         printf("\r\n");
    153 
    154     fflush(stdout);
    155 
    156     processInput();
    157 
    158     for (size_t i = 0; i < text.lines; i++)
    159         free(text.data[i]);
    160     free(text.data);
    161 
    162     // clear screen
    163     puts("\x1b[2J");
    164 
    165     return 0;
    166 }