summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.c76
-rw-r--r--src/til.c8
-rw-r--r--src/til_args.c7
-rw-r--r--src/til_args.h1
4 files changed, 81 insertions, 11 deletions
diff --git a/src/main.c b/src/main.c
index 38a99d0..f786818 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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),
diff --git a/src/til.c b/src/til.c
index 81a300e..1c2c2fe 100644
--- a/src/til.c
+++ b/src/til.c
@@ -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;
© All Rights Reserved