summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..c81f1d6
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,237 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <getopt.h>
+#include <unistd.h>
+#include "util/container.h"
+#include "util/file.h"
+#include "util/err.h"
+#include "scene.h"
+#include "parse.h"
+#include "render.h"
+#include "print.h"
+
+#include "draw/opengl.h"
+
+enum param_type
+{
+ PARAM_OUT = 1 << 0,
+ PARAM_SIZE = 1 << 1,
+ PARAM_FPS = 1 << 2,
+ PARAM_LIVE = 1 << 3,
+ PARAM_VERBOSE = 1 << 4,
+};
+
+struct params
+{
+ unsigned int flags;
+ str infile;
+ str outfile;
+ unsigned int size[2];
+ unsigned int fps;
+ option(double) live;
+};
+
+static void run(struct registry *reg, struct params *params)
+{
+ FILE *srcfile;
+ str srcpath;
+ if (array_eq(params->infile, S("-"))) {
+ srcfile = stdin;
+ srcpath = S("<stdin>");
+ } else {
+ srcpath = params->infile;
+ srcfile = file_open(params->infile, "r");
+ if (!srcfile)
+ eprintf("'%.*s': failed to open scene file\n", PSTR(params->infile));
+ }
+
+ str contents = file_read(srcfile);
+ fclose(srcfile);
+
+ struct source src;
+ struct scene scene;
+
+ source_init(&src, srcpath, contents);
+ scene_init(&scene);
+ parse_scene(&src, &scene, reg);
+
+ if (params->flags & PARAM_VERBOSE)
+ print_scene(stderr, &scene, SCENE_PARSED);
+
+ scene_bind(&scene, &src, reg);
+ registry_free(reg);
+
+ if (params->flags & PARAM_VERBOSE)
+ print_scene(stderr, &scene, SCENE_BOUND);
+
+ if (params->flags & PARAM_OUT)
+ scene.outfile = params->outfile;
+
+ if (params->flags & PARAM_SIZE) {
+ scene.size[0] = params->size[0];
+ scene.size[1] = params->size[1];
+ }
+
+ if (params->flags & PARAM_FPS)
+ scene.fps = params->fps;
+
+ if (params->flags & PARAM_LIVE) {
+ scene.live.present = true;
+ scene.live.data = params->live.present ? params->live.data : 1.0;
+ }
+
+ if (params->flags & PARAM_VERBOSE)
+ print_scene(stderr, &scene, SCENE_BOUND);
+
+ struct draw_backend *draw = &draw_backend_opengl;
+ draw->init(scene.size);
+ scene_load(&scene, &src, draw);
+
+ if (params->flags & PARAM_VERBOSE)
+ print_scene(stderr, &scene, SCENE_LOADED);
+
+ FILE *outfile;
+ if (scene.outfile.len) {
+ if (array_eq(scene.outfile, S("-"))) {
+ outfile = stdout;
+ } else {
+ outfile = file_open(scene.outfile, "wb");
+ if (!outfile)
+ eprintf("'%.*s': failed to open output\n", PSTR(scene.outfile));
+ }
+ } else {
+ if (isatty(STDOUT_FILENO))
+ eprintf("no output file specified; refusing to write video data to TTY\n");
+ outfile = stdout;
+ }
+
+ struct nut_writer out;
+ nut_write_start(&out, outfile, scene.fps, scene.size);
+
+ render_scene(&scene, draw, &out);
+
+ nut_write_end(&out);
+ fclose(outfile);
+ draw->cleanup();
+ scene_free(&scene);
+ source_free(&src);
+}
+
+static const char *help_text =
+"usage: %s [options] <scenefile>\n"
+" -o | --out <outfile> File to write resulting .nut video to. A default may be provided by the\n"
+" scene file. Otherwise, video is written to stdout unless stdout is a\n"
+" tty.\n"
+" -s | --size WxH Dimensions of the video. A default may be provided by the scene file.\n"
+" Otherwise, 100x100 is used.\n"
+" -f | --fps fps Fps of the video. A default may be provided by the scene file.\n"
+" Otherwise, 25 is used.\n"
+" -D | --define key=value Set variable key to value. Variables are declared in the scene file and\n"
+" may or may not have defaults. Variables without defaults have to\n"
+" be defined via command line.\n"
+" -l | --live [<seconds>] Enable live mode where video is rendered in real time instead of as\n"
+" fast as possible. A buffer time in seconds can be provided\n"
+" (default: 1.0). Video will be rendered ahead of time by this amount.\n"
+" -v | --verbose Print debugging information.\n"
+" -h | --help Display this help text.\n";
+
+int main(int argc, char **argv)
+{
+ struct registry reg = {};
+ struct params params = {};
+
+ static struct option options[] = {
+ {"out", required_argument, 0, 'o' },
+ {"size", required_argument, 0, 's' },
+ {"fps", required_argument, 0, 'f' },
+ {"define", required_argument, 0, 'D' },
+ {"live", optional_argument, 0, 'l' },
+ {"verbose", no_argument, 0, 'v' },
+ {"help", no_argument, 0, 'h' },
+ {0, 0, 0, 0 }
+ };
+
+ int c;
+ while ((c = getopt_long(argc, argv, "o:s:f:D:l::vh", options, nullptr)) != -1) {
+ if (c == 'l' && optarg == NULL && optind < argc && argv[optind][0] != '-')
+ optarg = argv[optind++];
+
+ str arg;
+ if (optarg)
+ arg = str_intro(optarg);
+
+ switch (c) {
+ case 'o':
+ params.flags |= PARAM_OUT;
+ params.outfile = arg;
+ break;
+ case 's': {
+ str s_w, s_h;
+ long w, h;
+ if (!str_split(arg, S("x"), &s_w, &s_h))
+ eprintf("'%.*s': size must contain an 'x'\n", PSTR(arg));
+ if (!str_parse_int(s_w, &w) || w <= 0)
+ eprintf("'%.*s': invalid width\n", PSTR(s_w));
+ if (!str_parse_int(s_w, &h) || h <= 0)
+ eprintf("'%.*s': invalid height\n", PSTR(s_h));
+ params.flags |= PARAM_SIZE;
+ params.size[0] = w;
+ params.size[1] = h;
+ break;
+ }
+ case 'f': {
+ long fps;
+ if (!str_parse_int(arg, &fps) || fps <= 0)
+ eprintf("'%.*s': invalid fps\n", PSTR(arg));
+ params.flags |= PARAM_FPS;
+ params.fps = fps;
+ break;
+ }
+ case 'D': {
+ str key, val;
+ if (!str_split(arg, S("="), &key, &val))
+ eprintf("'%.*s': definition must contain an '='\n", PSTR(arg));
+ bool succ;
+ map_insert(&reg.raw_vars, key, val, &succ);
+ if (!succ)
+ eprintf("'%.*s': already defined\n", PSTR(key));
+ break;
+ }
+ case 'l':
+ params.flags |= PARAM_LIVE;
+ if (optarg) {
+ double buffer;
+ if (!str_parse_double(arg, &buffer) || buffer < 0)
+ eprintf("'%.*s': invalid buffer time\n", PSTR(arg));
+ params.live.present = true;
+ params.live.data = buffer;
+ }
+ break;
+ case 'v':
+ params.flags |= PARAM_VERBOSE;
+ break;
+ case 'h':
+ fprintf(stderr, help_text, argv[0]);
+ exit(EXIT_SUCCESS);
+ break;
+ case '?':
+ eprintf("'%s': invalid option (see -h)\n", argv[optind]);
+ break;
+ case ':':
+ eprintf("'%s': missing argument (see -h)\n", argv[optind]);
+ break;
+ }
+ }
+
+ int remain_args = argc-optind;
+ if (remain_args > 1)
+ eprintf("too many arguments (see -h)\n");
+ if (remain_args < 1)
+ eprintf("missing scene file (see -h)\n");
+
+ params.infile = str_intro(argv[optind]);
+
+ run(&reg, &params);
+ return EXIT_SUCCESS;
+}