summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
authordavid <[email protected]>2026-10-13 07:43:48 -0300
committerdavid <[email protected]>2026-10-13 07:43:48 -0300
commitf0f954d463f7ab0e0ce792527e6567611ed67bf9 (patch)
tree862c75c87380f67c11c865797faa9cc0c25a5970 /src/main.c
parent3b16b2141ef5ce085ce50b737bc53ee3f7e556f3 (diff)
downloadmikusays-c-f0f954d463f7ab0e0ce792527e6567611ed67bf9.tar.gz
mikusays-c-f0f954d463f7ab0e0ce792527e6567611ed67bf9.zip
50% colors i'll do rainbow and whatever else i'm missing from the rust version later too lseepy rn
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c119
1 files changed, 116 insertions, 3 deletions
diff --git a/src/main.c b/src/main.c
index 3e50d4c..c6879b6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -160,7 +160,12 @@ static void print_help(void){
" -s, --style <STYLE> Style of the Miku art. A random one is chosen if not specified\n"
" -l, --list List all available art styles with their indices\n"
" -h, --help Print help\n"
- " -V, --version Print version\n");
+ " -V, --version Print version\n"
+ " --rainbow Apply a smooth rainbow gradient across the art\n"
+ " --saturation <0-100> Rainbow saturation (default: 100)\n"
+ " --brightness <0-100> Rainbow brightness (default: 50)\n"
+ " --color <COLOR> Set a single solid color\n"
+ " --no-color Disable all coloring\n");
}
static void list_all_art_styles(void){
@@ -177,6 +182,61 @@ static void list_all_art_styles(void){
printf("\nUse --style <number> to select a specific style, or omit for random selection.\n");
}
+static struct colorconfig colors;
+static int rainbow = 0, has_solid = 0;
+static rgbcolor solid;
+
+static int is_art_space(unsigned long codepoint){
+ return (codepoint >= 0x09 && codepoint <= 0x0D) || codepoint == 0x20 || codepoint == 0x85 || codepoint == 0xA0
+ || codepoint == 0x1680 || (codepoint >= 0x2000 && codepoint <= 0x200A) || codepoint == 0x2028
+ || codepoint == 0x2029 || codepoint == 0x202F || codepoint == 0x205F || codepoint == 0x3000;
+}
+
+static size_t count_colored_chars(char **art){
+ size_t total = 0;
+ for(int i = 0; art[i] != NULL; i++){
+ const unsigned char *p = (const unsigned char *)art[i];
+ while(*p){
+ unsigned long codepoint;
+ p += utf8_decode(p, &codepoint);
+ if(!is_art_space(codepoint)) total++;
+ }
+ }
+ return total;
+}
+
+static void print_art_line(const char *line, size_t *index, size_t total){
+ char esc[32];
+ const unsigned char *p = (const unsigned char *)line;
+
+ if(colors.mode == MODE_NONE || (!rainbow && !has_solid)){
+ fputs(line, stdout);
+ return;
+ }
+ if(!rainbow){//solid: one color for the whole line
+ rgb_to_fg_escape(solid, colors.mode, esc, sizeof(esc));
+ printf("%s%s\x1b[0m", esc, line);
+ return;
+ }
+ while(*p){//rainbow: one color per non-space char
+ unsigned long codepoint;
+ int len = utf8_decode(p, &codepoint);
+ if(is_art_space(codepoint)){
+ fwrite(p, 1, (size_t)len, stdout);
+ }else{
+ unsigned short hue = (unsigned short)((float)*index / (float)total * 360.0f + 0.5f);
+ rgbcolor c = hsl_to_rgb(hue, colors.saturation, colors.brightness);
+ rgb_to_fg_escape(c, colors.mode, esc, sizeof(esc));
+ printf("%s", esc);
+ fwrite(p, 1, (size_t)len, stdout);
+ fputs("\x1b[0m", stdout);
+ (*index)++;
+ }
+ p += len;
+ }
+}
+
+
void draw_miku_says(char *text, int style){
char **speech_bubble_lines = getbubblelines(text);
if(speech_bubble_lines == NULL)
@@ -255,6 +315,8 @@ void draw_miku_says(char *text, int style){
for(int i = 0; i < top_padding; i++){
printf("\n");
}
+ size_t color_index = 0;
+ size_t colored_total = count_colored_chars(miku_art);
for(int i = 0; all_content_lines[i] != NULL; i++){
int is_bubble_line = i < bubble_line_count;
@@ -268,7 +330,14 @@ void draw_miku_says(char *text, int style){
else
padding = overall_left_padding;
- printf("%*s%s\n", padding, "", all_content_lines[i]);
+ if(is_bubble_line){
+ printf("%*s%s\n", padding, "", all_content_lines[i]);
+ }else{
+ printf("%*s", padding, "");
+ print_art_line(all_content_lines[i], &color_index, colored_total);
+ putchar('\n');
+ }
+
}
for(int i = 0; i < top_padding; i++){
@@ -277,11 +346,13 @@ void draw_miku_says(char *text, int style){
freelines(speech_bubble_lines);
free(all_content_lines);
}
-
+
int main(int argc, char **argv){
srand(time(NULL));
getterminalsize();
+ colorconfig_init(&colors);
+
long style = -1; //-1 means random
int list = 0;
int ret = 0;
@@ -317,12 +388,54 @@ int main(int argc, char **argv){
goto out;
}
style = v < 0 ? -1 : v; //negative = random
+ } else if(strcmp(argv[i], "--no-color") == 0){
+ colors.mode = MODE_NONE;
+ } else if(strcmp(argv[i], "--rainbow") == 0){
+ rainbow = 1;
+ } else if(strcmp(argv[i], "--color") == 0){
+ if(i + 1 >= argc){
+ fprintf(stderr, "error: a value is required for '--color <COLOR>' but none was supplied\n");
+ ret = 2;
+ goto out;
+ }
+ i++;
+ ColorParseError err = parse_color(argv[i], &solid);
+ if(err != COLOR_OK){
+ fprintf(stderr, "error: invalid color '%s': %s; expected a named color, #RRGGBB, #RGB, R,G,B, hsl(H,S%%,L%%), or ANSI value 0-255\n",
+ argv[i], color_parse_error_message(err));
+ ret = 2;
+ goto out;
+ }
+ has_solid = 1;
+ } else if(strcmp(argv[i], "--saturation") == 0 || strcmp(argv[i], "--brightness") == 0){
+ const char *flag = argv[i];
+ if(i + 1 >= argc){
+ fprintf(stderr, "error: a value is required for '%s <0-100>' but none was supplied\n", flag);
+ ret = 2;
+ goto out;
+ }
+ i++;
+ char *end;
+ long v = strtol(argv[i], &end, 10);
+ if(*argv[i] == '\0' || *end != '\0' || v < 0 || v > 100){
+ fprintf(stderr, "error: invalid value '%s' for '%s <0-100>'\n", argv[i], flag);
+ ret = 2;
+ goto out;
+ }
+ if(flag[2] == 's') colors.saturation = (unsigned char)v;
+ else colors.brightness = (unsigned char)v;
} else {
strcat(text, argv[i]);
strcat(text, " ");
}
}
+ if(rainbow && has_solid){
+ fprintf(stderr, "error: --rainbow cannot be used with --color\n");
+ ret = 2;
+ goto out;
+ }
+
if(list){
list_all_art_styles();
} else if(text[0] == '\0'){