colourpicker

Simple Colour Picker written in C
git clone anongit@rnpnr.xyz:colourpicker.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

main.c (5914B)


      1 /* See LICENSE for copyright details */
      2 #include <raylib.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 
      6 #include "util.c"
      7 
      8 #ifdef _DEBUG
      9 #include <dlfcn.h>
     10 #include <fcntl.h>
     11 #include <sys/stat.h>
     12 #include <unistd.h>
     13 
     14 typedef struct timespec Filetime;
     15 
     16 global const char *libname = "./colourpicker.so";
     17 global void *libhandle;
     18 
     19 typedef void (do_colour_picker_fn)(ColourPickerCtx *, f32 dt, Vector2 window_pos, Vector2 mouse);
     20 global do_colour_picker_fn *do_colour_picker;
     21 
     22 function Filetime
     23 get_filetime(const char *name)
     24 {
     25 	struct stat sb;
     26 	if (stat(name, &sb) < 0)
     27 		return (Filetime){0};
     28 	return sb.st_mtim;
     29 }
     30 
     31 function s32
     32 compare_filetime(Filetime a, Filetime b)
     33 {
     34 	return (a.tv_sec - b.tv_sec) + (a.tv_nsec - b.tv_nsec);
     35 }
     36 
     37 function void
     38 load_library(const char *lib)
     39 {
     40 	/* NOTE: glibc is buggy gnuware so we need to check this */
     41 	if (libhandle)
     42 		dlclose(libhandle);
     43 	libhandle = dlopen(lib, RTLD_NOW|RTLD_LOCAL);
     44 	if (!libhandle)
     45 		fprintf(stderr, "do_debug: dlopen: %s\n", dlerror());
     46 
     47 	do_colour_picker = dlsym(libhandle, "do_colour_picker");
     48 	if (!do_colour_picker)
     49 		fprintf(stderr, "do_debug: dlsym: %s\n", dlerror());
     50 }
     51 
     52 function void
     53 do_debug(void)
     54 {
     55 	local_persist Filetime updated_time;
     56 	Filetime test_time = get_filetime(libname);
     57 	if (compare_filetime(test_time, updated_time)) {
     58 		load_library(libname);
     59 		updated_time = test_time;
     60 	}
     61 
     62 }
     63 #else
     64 
     65 #define do_debug(...)
     66 #include "colourpicker.c"
     67 
     68 #endif /* _DEBUG */
     69 
     70 global const char *argv0;
     71 
     72 function no_return void
     73 usage(void)
     74 {
     75 	printf("usage: %s [-h ????????] [-r ?.??] [-g ?.??] [-b ?.??] [-a ?.??]\n"
     76 	       "\t-h:          Hexadecimal Colour\n"
     77 	       "\t-r|-g|-b|-a: Floating Point Colour Value\n", argv0);
     78 	exit(1);
     79 }
     80 
     81 function f64
     82 try_read_f64(str8 s)
     83 {
     84 	f64 result = 0;
     85 	NumberConversion number = number_from_str8(s);
     86 	if (number.result != NumberConversionResult_Success) {
     87 		printf("invalid float colour literal: %.*s\n", (s32)s.length, s.data);
     88 		usage();
     89 	}
     90 
     91 	if (number.kind == NumberConversionKind_Float) result = number.F64;
     92 	else                                           result = (f64)number.S64;
     93 
     94 	return result;
     95 }
     96 
     97 extern s32
     98 main(s32 argc, char *argv[])
     99 {
    100 	argv0 = argv[0];
    101 
    102 	ColourPickerCtx ctx = {
    103 		.window_size = { .w = 640, .h = 860 },
    104 
    105 		.mode               = CPM_PICKER,
    106 		.stored_colour_kind = ColourKind_HSV,
    107 		.flags              = ColourPickerFlag_RefillTexture,
    108 
    109 		.text_input_state = {.idx = -1, .cursor_t = 1, .cursor_t_target = 0},
    110 		.mcs = {.mode_visible_t = 1, .next_mode = -1},
    111 		.ss  = {.scale_t = {1, 1, 1, 1}},
    112 
    113 		.bg = COLOUR_PICKER_BG,
    114 		.fg = COLOUR_PICKER_FG,
    115 
    116 		.colour        = STARTING_COLOUR,
    117 		.hover_colour  = HOVER_COLOUR,
    118 		.cursor_colour = CURSOR_COLOUR,
    119 
    120 		.colour_stack = {
    121 			.items  = {
    122 				{ .r = 0.04, .g = 0.04, .b = 0.04, .a = 1.00 },
    123 				{ .r = 0.92, .g = 0.88, .b = 0.78, .a = 1.00 },
    124 				{ .r = 0.34, .g = 0.23, .b = 0.50, .a = 1.00 },
    125 				{ .r = 0.59, .g = 0.11, .b = 0.25, .a = 1.00 },
    126 				{ .r = 0.20, .g = 0.60, .b = 0.24, .a = 1.00 },
    127 				{ .r = 0.14, .g = 0.29, .b = 0.72, .a = 1.00 },
    128 				{ .r = 0.11, .g = 0.59, .b = 0.36, .a = 1.00 },
    129 				{ .r = 0.72, .g = 0.37, .b = 0.19, .a = 1.00 },
    130 			},
    131 		},
    132 	};
    133 
    134 	for (u32 i = 0; i < CPM_LAST; i++)
    135 		ctx.mcs.buttons[i].hover_t = 1;
    136 
    137 	{
    138 		v4 rgb = hsv_to_rgb(ctx.colour);
    139 		for (s32 i = 1; i < argc; i++) {
    140 			if (argv[i][0] == '-') {
    141 				if (argv[i][1] == 'v') {
    142 					printf("colour picker %s\n", VERSION);
    143 					return 0;
    144 				}
    145 				if (argv[i + 1] == 0 || (argv[i][1] == 'h' && !IsHex(argv[i + 1][0])))
    146 					usage();
    147 
    148 				switch (argv[i][1]) {
    149 				case 'h':{
    150 					NumberConversion number = integer_from_str8(str8_from_c_str(argv[i + 1]), 1);
    151 					if (number.result == NumberConversionResult_Success) {
    152 						rgb = normalize_colour(number.U64);
    153 					} else {
    154 						printf("invalid hexadecimal colour: %s\n", argv[i + 1]);
    155 						usage();
    156 					}
    157 					ctx.colour = rgb_to_hsv(rgb);
    158 				}break;
    159 				case 'r':{rgb.r = try_read_f64(str8_from_c_str(argv[i + 1])); rgb.r = Clamp01(rgb.r);}break;
    160 				case 'g':{rgb.g = try_read_f64(str8_from_c_str(argv[i + 1])); rgb.g = Clamp01(rgb.g);}break;
    161 				case 'b':{rgb.b = try_read_f64(str8_from_c_str(argv[i + 1])); rgb.b = Clamp01(rgb.b);}break;
    162 				case 'a':{rgb.a = try_read_f64(str8_from_c_str(argv[i + 1])); rgb.a = Clamp01(rgb.a);}break;
    163 				default:{usage();}break;
    164 				}
    165 				i++;
    166 			} else {
    167 				usage();
    168 			}
    169 		}
    170 		ctx.colour          = rgb_to_hsv(rgb);
    171 		ctx.previous_colour = rgb;
    172 	}
    173 	ctx.pms.base_hue = ctx.colour.x;
    174 
    175 	#ifndef _DEBUG
    176 	SetTraceLogLevel(LOG_NONE);
    177 	#endif
    178 
    179 	SetConfigFlags(FLAG_VSYNC_HINT);
    180 	InitWindow(ctx.window_size.w, ctx.window_size.h, "Colour Picker");
    181 	/* NOTE: do this after initing so that the window starts out floating in tiling wm */
    182 	SetWindowMinSize(324, 324 * WINDOW_ASPECT_RATIO);
    183 	SetWindowState(FLAG_WINDOW_RESIZABLE);
    184 
    185 	{
    186 		Image icon;
    187 		RenderTexture icon_texture = LoadRenderTexture(128, 128);
    188 		BeginDrawing();
    189 		BeginTextureMode(icon_texture);
    190 		ClearBackground(ctx.bg);
    191 		DrawCircleGradient(64, 64, 48, rl_colour_from_normalized(hsv_to_rgb(ctx.colour)), ctx.bg);
    192 		DrawRing((Vector2){64, 64}, 45, 48, 0, 360, 128, SELECTOR_BORDER_COLOUR);
    193 		EndTextureMode();
    194 		EndDrawing();
    195 		icon = LoadImageFromTexture(icon_texture.texture);
    196 		SetWindowIcon(icon);
    197 		UnloadRenderTexture(icon_texture);
    198 		UnloadImage(icon);
    199 	}
    200 
    201 	ctx.font = LoadFont_lora_sb_0_inc();
    202 
    203 	while(!WindowShouldClose()) {
    204 		do_debug();
    205 
    206 		BeginDrawing();
    207 		ClearBackground(ctx.bg);
    208 		do_colour_picker(&ctx, GetFrameTime(), (Vector2){0}, GetMousePosition());
    209 		EndDrawing();
    210 	}
    211 
    212 	v4 rgba = {0};
    213 	switch (ctx.stored_colour_kind) {
    214 	case ColourKind_RGB: rgba = ctx.colour;             break;
    215 	case ColourKind_HSV: rgba = hsv_to_rgb(ctx.colour); break;
    216 	InvalidDefaultCase;
    217 	}
    218 
    219 	u32 packed_rgba = pack_rl_colour(rl_colour_from_normalized(rgba));
    220 	printf("0x%08X|{.r = %0.03f, .g = %0.03f, .b = %0.03f, .a = %0.03f}\n",
    221 	       packed_rgba, rgba.r, rgba.g, rgba.b, rgba.a);
    222 
    223 	return 0;
    224 }