#include "../defs/all.h" #include "../defs/mikuart.h" char **getmikuart(int style, int isteto){ if(isteto == 0){ if(style < 0) style = rand() % migucount; return mikuart[style]; } if(style < 0) style = rand() % tetocount; return tetoart[style]; } struct win { int winwidth; int winheight; }; static struct win term = {80, 24}; //if the function fails this is the defualt #ifdef _WIN32 #include static void getterminalsize(void) { CONSOLE_SCREEN_BUFFER_INFO info; HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); int found = GetConsoleScreenBufferInfo(console, &info); SetConsoleOutputCP(CP_UTF8); if(!found){ HANDLE fallback = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if(fallback != INVALID_HANDLE_VALUE){ found = GetConsoleScreenBufferInfo(fallback, &info); CloseHandle(fallback); } } if(found){ term.winwidth = info.srWindow.Right - info.srWindow.Left + 1; term.winheight = info.srWindow.Bottom - info.srWindow.Top + 1; } } #else #include #include #include static void getterminalsize(void) { struct winsize size_info; int tty_descriptor = -1; int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &size_info); if(status != 0){ tty_descriptor = open("/dev/tty", O_RDONLY); if(tty_descriptor >= 0) status = ioctl(tty_descriptor, TIOCGWINSZ, &size_info); } if(status == 0){ if(size_info.ws_col > 0) term.winwidth = size_info.ws_col; if(size_info.ws_row > 0) term.winheight = size_info.ws_row; } if(tty_descriptor >= 0) close(tty_descriptor); } #endif #define TERMINALWIDTH term.winwidth #define TERMINALHEIGHT term.winheight static int count; static void freelines(char **lines){ if(!lines) return; for(int i = 0; lines[i] != NULL; i++) free(lines[i]); free(lines); } static char **push_chunk(char **chunks, int *count, const char *start, size_t len){ chunks = realloc(chunks, (*count + 2) * sizeof(char *)); chunks[*count] = malloc(len + 1); memcpy(chunks[*count], start, len); chunks[*count][len] = '\0'; (*count)++; chunks[*count] = NULL; return chunks; } char **split_word_by_width(const char *word, int max_width){ char **chunks = NULL; int count = 0; size_t length = strlen(word); size_t start = 0, i = 0; int chunk_width = 0; while(i < length){ unsigned long codepoint; int bytes = utf8_decode((const unsigned char *)word + i, &codepoint); int width = codepoint_width(codepoint); if(i > start && chunk_width + width > max_width){ chunks = push_chunk(chunks, &count, word + start, i - start); start = i; chunk_width = 0; } chunk_width += width; i += bytes; } if(i > start) chunks = push_chunk(chunks, &count, word + start, i - start); return chunks; } char **wraptext(const char *text, int max, int *count){ char *copy = strdup(text); char *current_line = calloc(strlen(text)+1, sizeof(char)); char **lines = NULL; *count = 0; char *tok = strtok(copy, " \t\n"); while(tok != NULL){ if((size_t)displaywidth(current_line)+(size_t)displaywidth(tok) < (size_t)max){ if(current_line[0] != '\0'){ strcat(current_line, " "); } strcat(current_line, tok); } else{ if(displaywidth(tok) > max){ if(current_line[0] != '\0'){ lines = realloc(lines, (*count + 2) * sizeof(char *)); lines[*count] = strdup(current_line); (*count)++; lines[*count] = NULL; } char **chunks = split_word_by_width(tok, max); int n = 0; while(chunks[n] != NULL) n++; for(int i = 0; i < n - 1; i++){ lines = realloc(lines, (*count + 2) * sizeof(char *)); lines[*count] = chunks[i]; (*count)++; lines[*count] = NULL; } strcpy(current_line, chunks[n - 1]); free(chunks[n - 1]); free(chunks); } else{ if(current_line[0] != '\0'){ lines = realloc(lines, (*count + 2) * sizeof(char *)); lines[*count] = strdup(current_line); (*count)++; lines[*count] = NULL; } strcpy(current_line, tok); } } tok = strtok(NULL, " \t\n"); } if(current_line[0] != '\0'){ lines = realloc(lines, (*count + 2) * sizeof(char *)); lines[*count] = strdup(current_line); (*count)++; lines[*count] = NULL; } free(copy); free(current_line); return lines; } #define MAX_BUBBLE_WIDTH 50 #define MIN_BUBBLE_WIDTH 20 char **getbubblelines(char *text){ char **templines = wraptext(text, MAX_BUBBLE_WIDTH - 4, &count); size_t content_width = 0; for (int i = 0; templines != NULL && templines[i] != NULL; i++) { size_t current_len = displaywidth(templines[i]); if (current_len > content_width) content_width = current_len; } freelines(templines); int bubble_width = content_width + 4; if(bubble_width < MIN_BUBBLE_WIDTH) bubble_width = MIN_BUBBLE_WIDTH; if(bubble_width > MAX_BUBBLE_WIDTH) bubble_width = MAX_BUBBLE_WIDTH; char **lines = wraptext(text, bubble_width - 4, &count); const char *top_left = "╭"; const char *top_right = "╮"; const char *bottom_left = "╰"; const char *bottom_right = "╯"; const char *horizontal = "─"; const char *vertical = "│"; char **bubblelines = calloc(count + 5, sizeof(char *)); bubblelines[0] = malloc(strlen(top_left) + strlen(horizontal) * bubble_width + strlen(top_right) + 1); strcpy(bubblelines[0], top_left); for (int i = 0; i < bubble_width; i++) strcat(bubblelines[0], horizontal); strcat(bubblelines[0], top_right); for (int i = 0; lines != NULL && lines[i] != NULL; i++) { int line_width = displaywidth(lines[i]); int padding_needed = (bubble_width - 2) - line_width; if (padding_needed < 0) padding_needed = 0; int left_padding = padding_needed / 2; int right_padding = padding_needed - left_padding; bubblelines[i + 1] = malloc(strlen(vertical) * 2 + 2 + strlen(lines[i]) + padding_needed + 1); sprintf(bubblelines[i + 1], "%s %*s%s%*s %s",vertical, left_padding, "", lines[i], right_padding, "", vertical); } bubblelines[count+1] = malloc(strlen(bottom_left) + strlen(horizontal) * bubble_width + strlen(bottom_right) + 1); strcpy(bubblelines[count+1], bottom_left); for (int i = 0; i < bubble_width; i++) strcat(bubblelines[count+1], horizontal); strcat(bubblelines[count+1], bottom_right); bubblelines[count + 2] = strdup("\\"); bubblelines[count + 3] = strdup(" \\"); freelines(lines); return bubblelines; } static void print_help(void){ printf("A \"cowsay\" clone with Hatsune Miku ASCII art and speech bubbles.\n\n" "Usage: mikusays [OPTIONS] [TEXT]\n\n" "Arguments:\n" " [TEXT] Text to display in the speech bubble\n\n" "Options:\n" " -s, --style