dot_testing/c/tmessage.c

149 lines
3.1 KiB
C

#include <sys/ioctl.h>
#include <ctype.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#define ESC_CLEAR "\033[2J"
#define ESC_CUSHOME "\033[H"
#define ESC_NEWLINE "\033[E\033[1C"
const char MSG_OK[] = "(O)k";
const char MSG_CANCEL[] = "(C)ancel";
typedef struct winsize ws;
int lines, cols;
char *message;
size_t message_len;
struct termios original_termios;
void render(char *message, int message_len, int lines, int cols);
void rawmode_start() {
tcgetattr(STDIN_FILENO, &original_termios);
struct termios raw = original_termios;
raw.c_lflag &= ~(ECHO | ICANON);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
void rawmode_stop() {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &original_termios);
printf("\n\n");
fflush(stdout);
}
ws *getWinSize(void) {
/* we're only going to use these values once per invocation,
* so it's fine that it's static. */
static ws w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
return &w;
}
void handler(int signal, siginfo_t *info, void *context) {
ws *w = getWinSize();
lines = w->ws_row;
cols = w->ws_col;
render(message, message_len, lines, cols);
return;
}
void termhandler(int signal, siginfo_t *info, void *context) {
rawmode_stop();
return;
}
int calculate_number_of_lines(int length, int cols) {
/* cols - 1 accounts for right padding */
return length / (cols - 1) + 1;
}
void go_to_initial_message_printing_pos(void) {
printf(ESC_CUSHOME "\033[1B\033[1C");
}
void print_message(char *message, int messagelen, int cols) {
int linecount = calculate_number_of_lines(messagelen, cols);
int adjcols = cols - 2;
int offset = 1;
for(int character = 0; character < messagelen; character++) {
if(character == adjcols * offset) {
printf(ESC_NEWLINE);
offset++;
}
putchar(message[character]);
}
}
void render(char *message, int messagelen, int lines, int cols) {
int cancel_length = sizeof(MSG_CANCEL) / sizeof(MSG_CANCEL[0]);
/* print the stuff */
printf(ESC_CLEAR "" ESC_CUSHOME);
go_to_initial_message_printing_pos();
print_message(message, message_len, cols);
printf(ESC_NEWLINE);
printf(ESC_NEWLINE);
printf("%s", MSG_OK);
printf("\033[1D\033[%iC\033[%iD%s", cols, cancel_length, MSG_CANCEL);
fflush(stdout);
}
int main(int argc, char **argv) {
int cancel_length = strlen(MSG_CANCEL);
char c;
struct sigaction resizeaction = {};
struct sigaction termaction = {};
ws *w;
/* check if we have a message */
if(argc > 1) {
message = argv[1]; /* second argument's a message */
message_len = strlen(message);
} else {
return 1;
}
/* setup sigaction handlers */
resizeaction.sa_sigaction = &handler;
if(sigaction(SIGWINCH, &resizeaction, NULL) == -1) {
return 1;
}
termaction.sa_sigaction = &termhandler;
if(sigaction(SIGTERM, &termaction, NULL) == -1) {
return 1;
}
rawmode_start();
/* get window properties */
w = getWinSize();
lines = w->ws_row;
cols = w->ws_col;
render(message, message_len, lines, cols);
for(;;) {
read(STDIN_FILENO, &c, 1);
if(c == 'o') {
return 2;
rawmode_stop();
} else if(c == 'c') {
return 3;
rawmode_stop();
}
}
return 0;
}