diff options
-rw-r--r-- | src/main.c | 76 | ||||
-rw-r--r-- | src/til.c | 8 | ||||
-rw-r--r-- | src/til_args.c | 7 | ||||
-rw-r--r-- | src/til_args.h | 1 |
4 files changed, 81 insertions, 11 deletions
@@ -66,6 +66,7 @@ typedef struct setup_t { til_setup_t *module_setup; til_settings_t *video_settings; til_setup_t *video_setup; + unsigned seed; } setup_t; /* FIXME: this is unnecessarily copy-pasta, i think modules should just be made @@ -136,13 +137,84 @@ static int setup_video(til_settings_t *settings, til_setting_t **res_setting, co } +/* TODO: move to til.c */ +/* parse a hexadecimal seed with an optional leading 0x prefix into a libc srand()-appropriate machine-dependent sized unsigned int */ +/* returns -errno on any failure (including overflow), 0 on success. */ +static int parse_seed(const char *in, unsigned *res_seed) +{ + unsigned seed = 0; + + assert(in); + assert(res_seed); + + if (in[0] == '0' && (in[1] == 'x' || in[1] == 'X')) /* accept and ignore leading "0[xX]" */ + in += 2; + + for (int i = 0; *in && i < sizeof(*res_seed) * 2;) { + uint8_t h = 0; + + seed <<= 8; + + for (int j = 0; *in && j < 2; in++, j++, i++) { + h <<= 4; + + switch (*in) { + case '0'...'9': + h |= (*in) - '0'; + break; + + case 'a'...'f': + h |= (*in) - 'a' + 10; + break; + + case 'A'...'F': + h |= (*in) - 'A' + 10; + break; + + default: + return -EINVAL; + } + } + + seed |= h; + } + + if (*in) + return -EOVERFLOW; + + *res_seed = seed; + + return 0; +} + + +/* TODO: move to til.c, setup_t in general should just become til_setup_t. + * the sticking point is setup_interactively() is very rototiller-specific, so it needs + * to be turned into a caller-supplied callback or something. + */ /* turn args into settings, automatically applying defaults if appropriate, or interactively if appropriate. */ /* returns negative value on error, 0 when settings unchanged from args, 1 when changed */ /* on error, *res_failed_desc _may_ be assigned with something useful. */ static int setup_from_args(til_args_t *args, setup_t *res_setup, const til_setting_desc_t **res_failed_desc) { int r = -ENOMEM, changes = 0; - setup_t setup = {}; + setup_t setup = { .seed = time(NULL) + getpid() }; + + assert(args); + assert(res_setup); + + if (args->seed) { + r = parse_seed(args->seed, &setup.seed); + if (r < 0) + goto _err; + } + + /* FIXME TODO: this is gross! but we want to seed the PRNG before we do any actual setup + * in case we're randomizing settings. + * Maybe it makes more sense to just add a TIL_SEED env variable and let til_init() getenv("TIL_SEED") + * and do all this instead of setup_from_args(). This'll do for now. + */ + srand(setup.seed); setup.module_settings = til_settings_new(args->module); if (!setup.module_settings) @@ -296,7 +368,7 @@ int main(int argc, const char *argv[]) gettimeofday(&rototiller.start_tv, NULL); exit_if((r = til_module_create_context( - rototiller.module, 0, + rototiller.module, setup.seed, get_ticks(&rototiller.start_tv, &rototiller.start_tv, rototiller.ticks_offset), @@ -80,14 +80,6 @@ static const til_module_t *modules[] = { /* initialize rototiller (create rendering threads) */ int til_init(void) { - /* Various modules seed srand(), just do it here so they don't need to. - * At some point in the future this might become undesirable, if reproducible - * pseudo-randomized output is actually desirable. But that should probably be - * achieved using rand_r() anyways, since modules can't prevent others from playing - * with srand(). - */ - srand(time(NULL) + getpid()); - if (!(til_threads = til_threads_create())) return -errno; diff --git a/src/til_args.c b/src/til_args.c index e15150f..8969d89 100644 --- a/src/til_args.c +++ b/src/til_args.c @@ -9,7 +9,9 @@ * ./rototiller --video=drm,dev=/dev/dri/card3,connector=VGA-1,mode=640x480@60 * ./rototiller --video=sdl,size=640x480 * ./rototiller --module=roto,foo=bar,module=settings - * ./rototiller --defaults + * ./rototiller --defaults // use default settings where unspecified + * ./rototiller --go // don't show args and wait for user input before proceeding + * ./rototiller --seed=0xdeadbeef // explicitly set global random seed instead of generating one * * unrecognized arguments trigger an -EINVAL error, unless res_{argc,argv} are non-NULL * where a new argv will be allocated and populated with the otherwise invalid arguments @@ -44,6 +46,8 @@ static int args_parse(int argc, const char *argv[], til_args_t *res_args, int *r res_args->video = &argv[i][8]; } else if (!strncasecmp("--module=", argv[i], 9)) { res_args->module = &argv[i][9]; + } else if (!strncasecmp("--seed=", argv[i], 7)) { + res_args->seed = &argv[i][7]; } else if (!strcasecmp("--defaults", argv[i])) { res_args->use_defaults = 1; } else if (!strcasecmp("--help", argv[i])) { @@ -84,6 +88,7 @@ int til_args_help(FILE *out) " --go start rendering immediately upon fulfilling all required settings\n" " --help this help\n" " --module= module settings\n" + " --seed= seed to use for all PRNG in hexadecimal (e.g. 0xdeadbeef)\n" " --video= video settings\n" ); } diff --git a/src/til_args.h b/src/til_args.h index efe1e3c..d45b619 100644 --- a/src/til_args.h +++ b/src/til_args.h @@ -6,6 +6,7 @@ typedef struct til_args_t { const char *module; const char *video; + const char *seed; unsigned use_defaults:1; unsigned help:1; |