diff options
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/drm_fb.c | 372 | ||||
-rw-r--r-- | src/drmsetup.c | 162 | ||||
-rw-r--r-- | src/drmsetup.h | 10 | ||||
-rw-r--r-- | src/fb.c | 12 | ||||
-rw-r--r-- | src/fb.h | 7 | ||||
-rw-r--r-- | src/rototiller.c | 250 |
7 files changed, 598 insertions, 217 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index acb5c16..a9cee3e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = modules bin_PROGRAMS = rototiller -rototiller_SOURCES = drmsetup.c drmsetup.h drm_fb.c drm_fb.h fb.c fb.h fps.c fps.h rototiller.c rototiller.h settings.h settings.c setup.h setup.c threads.c threads.h util.c util.h +rototiller_SOURCES = drm_fb.c drm_fb.h fb.c fb.h fps.c fps.h rototiller.c rototiller.h settings.h settings.c setup.h setup.c threads.c threads.h util.c util.h rototiller_LDADD = @ROTOTILLER_LIBS@ -lm modules/julia/libjulia.a modules/plasma/libplasma.a modules/ray/libray.a modules/roto/libroto.a modules/sparkler/libsparkler.a modules/stars/libstars.a rototiller_CPPFLAGS = @ROTOTILLER_CFLAGS@ diff --git a/src/drm_fb.c b/src/drm_fb.c index 99169de..855fce1 100644 --- a/src/drm_fb.c +++ b/src/drm_fb.c @@ -1,25 +1,28 @@ +#define _GNU_SOURCE /* for asprintf() */ +#include <assert.h> #include <fcntl.h> #include <inttypes.h> +#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mman.h> +#include <unistd.h> #include <xf86drm.h> #include <xf86drmMode.h> #include "fb.h" +#include "settings.h" #include "util.h" - /* drm fb backend, everything drm-specific in rototiller resides here. */ typedef struct drm_fb_t { int drm_fd; - uint32_t crtc_id; - uint32_t *connectors; - int n_connectors; - drmModeModeInfoPtr mode; + drmModeCrtc *crtc; + drmModeConnector *connector; + drmModeModeInfo *mode; } drm_fb_t; typedef struct drm_fb_page_t drm_fb_page_t; @@ -31,28 +34,362 @@ struct drm_fb_page_t { uint32_t drm_fb_id; }; +typedef struct drm_fb_setup_t { + const char *dev; + const char *connector; + const char *mode; +} drm_fb_setup_t; + + +static const char * connector_type_name(uint32_t type) { + static const char *connector_types[] = { + "Unknown", + "VGA", + "DVII", + "DVID", + "DVIA", + "Composite", + "SVIDEO", + "LVDS", + "Component", + "SPinDIN", + "DisplayPort", + "HDMIA", + "HDMIB", + "TV", + "eDP", + "VIRTUAL", + "DSI" + }; + + assert(type < nelems(connector_types)); + + return connector_types[type]; +} + + +static setting_desc_t * dev_desc_generator(void *setup_context) +{ + return setting_desc_new("DRM Device Path", + "dev", + "/dev/dri/card[0-9]", + "/dev/dri/card0", + NULL, + NULL); +} + + +/* returns a NULL-terminated array of drm connectors */ +static const char ** get_connectors(const char *dev) +{ + int counts[64] = {}; /* assuming this is big enough */ + char **connectors; + int i, fd; + drmModeRes *res; + + assert(dev); + + fd = open(dev, O_RDWR); + if (fd == -1) + return NULL; + + res = drmModeGetResources(fd); + if (!res) { + close(fd); + + return NULL; + } + + connectors = calloc(res->count_connectors + 1, sizeof(*connectors)); + if (!connectors) { + close(fd); + + return NULL; + } + + for (i = 0; i < res->count_connectors; i++) { + drmModeConnector *con; + + con = drmModeGetConnector(fd, res->connectors[i]); + if (!con) { + close(fd); + + return NULL; + } + + counts[con->connector_type]++; + asprintf(&connectors[i], "%s-%i", connector_type_name(con->connector_type), counts[con->connector_type]); /* TODO: errors */ + + drmModeFreeConnector(con); + } + + drmModeFreeResources(res); + close(fd); + + return (const char **)connectors; +} + + +static void free_strv(const char **strv) +{ + int i; + + for (i = 0; strv[i]; i++) + free((void *)strv[i]); + + free((void *)strv); +} + + +static setting_desc_t * connector_desc_generator(void *setup_context) +{ + drm_fb_setup_t *s = setup_context; + const char **connectors; + setting_desc_t *desc; + + connectors = get_connectors(s->dev); + if (!connectors) + return NULL; + + desc = setting_desc_new("DRM Connector", + "connector", + "[a-zA-Z0-9]+", + connectors[0], + connectors, + NULL); + + free_strv(connectors); + + return desc; +} + + +static drmModeConnector * lookup_connector(int fd, const char *connector) +{ + int i, counts[64] = {}; /* assuming this is big enough */ + drmModeConnector *con = NULL; + drmModeRes *res; + + res = drmModeGetResources(fd); + if (!res) + goto _out; + + for (i = 0; i < res->count_connectors; i++) { + char *str; + + con = drmModeGetConnector(fd, res->connectors[i]); + if (!con) + goto _out_res; + + counts[con->connector_type]++; + asprintf(&str, "%s-%i", connector_type_name(con->connector_type), counts[con->connector_type]); /* TODO: errors */ -drm_fb_t * drm_fb_new(int drm_fd, uint32_t crtc_id, uint32_t *connectors, int n_connectors, drmModeModeInfoPtr mode) + if (!strcasecmp(str, connector)) { + free(str); + + break; + } + + free(str); + drmModeFreeConnector(con); + con = NULL; + } + +_out_res: + drmModeFreeResources(res); +_out: + return con; +} + + +/* returns a NULL-terminated array of drm modes for the supplied device and connector */ +static const char ** get_modes(const char *dev, const char *connector) +{ + char **modes = NULL; + int i, fd; + drmModeConnector *con; + + assert(dev); + assert(connector); + + fd = open(dev, O_RDWR); + if (fd == -1) + goto _out; + + con = lookup_connector(fd, connector); + if (!con) + goto _out_fd; + + modes = calloc(con->count_modes + 1, sizeof(*modes)); + if (!modes) + goto _out_con; + + for (i = 0; i < con->count_modes; i++) + asprintf(&modes[i], "%s@%"PRIu32, con->modes[i].name, con->modes[i].vrefresh); + +_out_con: + drmModeFreeConnector(con); +_out_fd: + close(fd); +_out: + return (const char **)modes; +} + + +static setting_desc_t * mode_desc_generator(void *setup_context) +{ + drm_fb_setup_t *s = setup_context; + setting_desc_t *desc; + const char **modes; + + modes = get_modes(s->dev, s->connector); + if (!modes) + return NULL; + + desc = setting_desc_new("DRM Video Mode", + "mode", + "[0-9]+[xX][0-9]+@[0-9]+", + modes[0], + modes, + NULL); + + free_strv(modes); + + return desc; +} + + +/* setup is called repeatedly as settings is constructed, until 0 is returned. */ +/* a negative value is returned on error */ +/* positive value indicates another setting is needed, described in next_setting */ +int drm_fb_setup(settings_t *settings, setting_desc_t **next_setting) +{ + drm_fb_setup_t context = {}; + setting_desc_generator_t generators[] = { + { + .key = "dev", + .value_ptr = &context.dev, + .func = dev_desc_generator + }, { + .key = "connector", + .value_ptr = &context.connector, + .func = connector_desc_generator + }, { + .key = "mode", + .value_ptr = &context.mode, + .func = mode_desc_generator + }, + }; + + if (!drmAvailable()) + return -ENOSYS; + + return settings_apply_desc_generators(settings, generators, nelems(generators), &context, next_setting); +} + + +/* lookup a mode string in the given connector returning its respective modeinfo */ +static drmModeModeInfo * lookup_mode(drmModeConnector *connector, const char *mode) +{ + int i; + + assert(connector); + assert(mode); + + for (i = 0; i < connector->count_modes; i++) { + char *str; + + asprintf(&str, "%s@%"PRIu32, connector->modes[i].name, connector->modes[i].vrefresh); + if (!strcasecmp(str, mode)) { + free(str); + + return &connector->modes[i]; + } + + free(str); + } + + return NULL; +} + + +/* prepare the drm context for use with the supplied settings */ +void * drm_fb_init(settings_t *settings) { drm_fb_t *c; + const char *dev; + const char *connector; + const char *mode; + drmModeEncoder *enc; + + assert(settings); + + if (!drmAvailable()) + goto _err; + + dev = settings_get_value(settings, "dev"); + if (!dev) + goto _err; + + connector = settings_get_value(settings, "connector"); + if (!connector) + goto _err; + + mode = settings_get_value(settings, "mode"); + if (!mode) + goto _err; c = calloc(1, sizeof(drm_fb_t)); if (!c) - return NULL; + goto _err; + + c->drm_fd = open(dev, O_RDWR); + if (c->drm_fd < 0) + goto _err_ctxt; + + c->connector = lookup_connector(c->drm_fd, connector); + if (!c->connector) + goto _err_fd; + + c->mode = lookup_mode(c->connector, mode); + if (!c->mode) + goto _err_con; - c->drm_fd = drm_fd; - c->crtc_id = crtc_id; - c->connectors = connectors; - c->n_connectors = n_connectors; - c->mode = mode; + enc = drmModeGetEncoder(c->drm_fd, c->connector->encoder_id); + if (!enc) + goto _err_con; + + c->crtc = drmModeGetCrtc(c->drm_fd, enc->crtc_id); + if (!c->crtc) + goto _err_enc; + + drmModeFreeEncoder(enc); return c; + +_err_enc: + drmModeFreeEncoder(enc); +_err_con: + drmModeFreeConnector(c->connector); +_err_fd: + close(c->drm_fd); +_err_ctxt: + free(c); +_err: + return NULL; } -void drm_fb_free(drm_fb_t *context) +void drm_fb_shutdown(void *context) { - free(context); + drm_fb_t *c = context; + + assert(c); + + close(c->drm_fd); + drmModeFreeConnector(c->connector); + drmModeFreeCrtc(c->crtc); + free(c); } @@ -61,7 +398,7 @@ static int drm_fb_acquire(void *context, void *page) drm_fb_t *c = context; drm_fb_page_t *p = page; - return drmModeSetCrtc(c->drm_fd, c->crtc_id, p->drm_fb_id, 0, 0, c->connectors, c->n_connectors, c->mode); + return drmModeSetCrtc(c->drm_fd, c->crtc->crtc_id, p->drm_fb_id, 0, 0, &c->connector->connector_id, 1, c->mode); } @@ -141,7 +478,7 @@ static int drm_fb_page_flip(void *context, void *page) drm_fb_t *c = context; drm_fb_page_t *p = page; - if (drmModePageFlip(c->drm_fd, c->crtc_id, p->drm_fb_id, DRM_MODE_PAGE_FLIP_EVENT, NULL) < 0) + if (drmModePageFlip(c->drm_fd, c->crtc->crtc_id, p->drm_fb_id, DRM_MODE_PAGE_FLIP_EVENT, NULL) < 0) return -1; return drmHandleEvent(c->drm_fd, &drm_ev_ctx); @@ -149,6 +486,9 @@ static int drm_fb_page_flip(void *context, void *page) fb_ops_t drm_fb_ops = { + .setup = drm_fb_setup, + .init = drm_fb_init, + .shutdown = drm_fb_shutdown, .acquire = drm_fb_acquire, .release = drm_fb_release, .page_alloc = drm_fb_page_alloc, diff --git a/src/drmsetup.c b/src/drmsetup.c deleted file mode 100644 index 3670974..0000000 --- a/src/drmsetup.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Rudimentary drm setup dialog... this is currently a very basic stdio thingy. - */ - -#include <assert.h> -#include <fcntl.h> -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <xf86drm.h> -#include <xf86drmMode.h> - -#include "util.h" - -static const char * encoder_type_name(uint32_t type) { - static const char *encoder_types[] = { - "None", - "DAC", - "TMDS", - "LVDAC", - "VIRTUAL", - "DSI" - }; - - assert(type < nelems(encoder_types)); - - return encoder_types[type]; -} - - -static const char * connector_type_name(uint32_t type) { - static const char *connector_types[] = { - "Unknown", - "VGA", - "DVII", - "DVID", - "DVIA", - "Composite", - "SVIDEO", - "LVDS", - "Component", - "SPinDIN", - "DisplayPort", - "HDMIA", - "HDMIB", - "TV", - "eDP", - "VIRTUAL", - "DSI" - }; - - assert(type < nelems(connector_types)); - - return connector_types[type]; -} - - -static const char * connection_type_name(int type) { - static const char *connection_types[] = { - [1] = "Connected", - "Disconnected", - "Unknown" - }; - - assert(type < nelems(connection_types)); - - return connection_types[type]; -} - - -/* interactively setup the drm device, store the selections */ -void drm_setup(int *res_drm_fd, uint32_t *res_crtc_id, uint32_t *res_connector_id, drmModeModeInfoPtr *res_mode) -{ - int drm_fd, i, connected; - drmVersionPtr drm_ver; - drmModeResPtr drm_res; - drmModeConnectorPtr drm_con; - drmModeEncoderPtr drm_enc; - drmModeCrtcPtr drm_crtc; - char dev[256]; - int connector_num, mode_num; - - pexit_if(!drmAvailable(), - "drm unavailable"); - - ask_string(dev, sizeof(dev), "DRM device", "/dev/dri/card0"); - - pexit_if((drm_fd = open(dev, O_RDWR)) < 0, - "unable to open drm device \"%s\"", dev); - - pexit_if(!(drm_ver = drmGetVersion(drm_fd)), - "unable to get drm version"); - - printf("\nVersion: %i.%i.%i\nName: \"%.*s\"\nDate: \"%.*s\"\nDescription: \"%.*s\"\n\n", - drm_ver->version_major, - drm_ver->version_minor, - drm_ver->version_patchlevel, - drm_ver->name_len, - drm_ver->name, - drm_ver->date_len, - drm_ver->date, - drm_ver->desc_len, - drm_ver->desc); - - pexit_if(!(drm_res = drmModeGetResources(drm_fd)), - "unable to get drm resources"); - - printf("\nConnectors\n"); - connected = 0; - for (i = 0; i < drm_res->count_connectors; i++) { - - pexit_if(!(drm_con = drmModeGetConnector(drm_fd, drm_res->connectors[i])), - "unable to get connector %x", (int)drm_res->connectors[i]); - - if (!drm_con->encoder_id) { - continue; - } - - pexit_if(!(drm_enc = drmModeGetEncoder(drm_fd, drm_con->encoder_id)), - "unable to get encoder %x", (int)drm_con->encoder_id); - - connected++; - - printf(" %i: %s (%s%s%s)\n", - i, connector_type_name(drm_con->connector_type), - connection_type_name(drm_con->connection), - drm_con->encoder_id ? " via " : "", - drm_con->encoder_id ? encoder_type_name(drm_enc->encoder_type) : ""); - /* TODO show mmWidth/mmHeight? */ - } - - exit_if(!connected, - "No connectors available, try different card or my bug?"); - ask_num(&connector_num, drm_res->count_connectors, "Select connector", 0); // TODO default? - - pexit_if(!(drm_con = drmModeGetConnector(drm_fd, drm_res->connectors[connector_num])), - "unable to get connector %x", (int)drm_res->connectors[connector_num]); - pexit_if(!(drm_enc = drmModeGetEncoder(drm_fd, drm_con->encoder_id)), - "unable to get encoder %x", (int)drm_con->encoder_id); - - pexit_if(!(drm_crtc = drmModeGetCrtc(drm_fd, drm_enc->crtc_id)), - "unable to get crtc %x", (int)drm_enc->crtc_id); - - *res_drm_fd = drm_fd; - *res_crtc_id = drm_crtc->crtc_id; - *res_connector_id = drm_con->connector_id; - - printf("\nModes\n"); - for (i = 0; i < drm_con->count_modes; i++) { - printf(" %i: %s @ %"PRIu32"Hz\n", - i, drm_con->modes[i].name, - drm_con->modes[i].vrefresh); - } - ask_num(&mode_num, drm_con->count_modes, "Select mode", 0); // TODO default to &drm_crtc->mode? - - *res_mode = &drm_con->modes[mode_num]; -} diff --git a/src/drmsetup.h b/src/drmsetup.h deleted file mode 100644 index 61af2d5..0000000 --- a/src/drmsetup.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _DRM_SETUP_H -#define _DRM_SETUP_H - -#include <stddef.h> /* xf86drmMode.h uses size_t without including stddef.h, sigh */ -#include <stdint.h> -#include <xf86drmMode.h> - -void drm_setup(int *res_drm_fd, uint32_t *res_crtc_id, uint32_t *res_connector_id, drmModeModeInfoPtr *res_mode); - -#endif @@ -5,6 +5,7 @@ #include <stdint.h> #include "fb.h" +#include "settings.h" #include "util.h" /* Copyright (C) 2016-2017 Vito Caputo <vcaputo@pengaru.com> */ @@ -240,6 +241,9 @@ void fb_free(fb_t *fb) /* TODO: free all the pages */ + if (fb->ops->shutdown && fb->ops_context) + fb->ops->shutdown(fb->ops_context); + pthread_mutex_destroy(&fb->ready_mutex); pthread_cond_destroy(&fb->ready_cond); pthread_mutex_destroy(&fb->inactive_mutex); @@ -250,7 +254,7 @@ void fb_free(fb_t *fb) /* create a new fb instance */ -fb_t * fb_new(const fb_ops_t *ops, void *context, int n_pages) +fb_t * fb_new(const fb_ops_t *ops, settings_t *settings, int n_pages) { _fb_page_t *page; fb_t *fb; @@ -271,7 +275,11 @@ fb_t * fb_new(const fb_ops_t *ops, void *context, int n_pages) return NULL; fb->ops = ops; - fb->ops_context = context; + if (ops->init) { + fb->ops_context = ops->init(settings); + if (!fb->ops_context) + goto fail; + } for (i = 0; i < n_pages; i++) fb_page_new(fb); @@ -4,6 +4,8 @@ #include <stdint.h> #include <string.h> +#include "settings.h" + /* All renderers should target fb_fragment_t, which may or may not represent * a full-screen mmap. Helpers are provided for subdividing fragments for * concurrent renderers. @@ -28,6 +30,9 @@ typedef struct fb_page_t { /* Supply this struct to fb_new() with the appropriate context */ typedef struct fb_ops_t { + int (*setup)(settings_t *settings, setting_desc_t **next); + void * (*init)(settings_t *settings); + void (*shutdown)(void *context); int (*acquire)(void *context, void *page); void (*release)(void *context); void * (*page_alloc)(void *context, fb_page_t *res_page); @@ -41,7 +46,7 @@ fb_page_t * fb_page_get(fb_t *fb); void fb_page_put(fb_t *fb, fb_page_t *page); void fb_free(fb_t *fb); void fb_get_put_pages_count(fb_t *fb, unsigned *count); -fb_t * fb_new(const fb_ops_t *ops, void *context, int n_pages); +fb_t * fb_new(const fb_ops_t *ops, settings_t *settings, int n_pages); void fb_fragment_divide(fb_fragment_t *fragment, unsigned n_fragments, fb_fragment_t fragments[]); int fb_fragment_slice_single(const fb_fragment_t *fragment, unsigned n_fragments, unsigned num, fb_fragment_t *res_fragment); int fb_fragment_tile_single(const fb_fragment_t *fragment, unsigned tile_size, unsigned num, fb_fragment_t *res_fragment); diff --git a/src/rototiller.c b/src/rototiller.c index 9362d19..e9bccfa 100644 --- a/src/rototiller.c +++ b/src/rototiller.c @@ -11,7 +11,8 @@ #include <xf86drmMode.h> #include "drm_fb.h" -#include "drmsetup.h" +#include "settings.h" +#include "setup.h" #include "fb.h" #include "fps.h" #include "rototiller.h" @@ -26,6 +27,8 @@ * another page so we can begin rendering another frame before vsync. With * just two pages we end up twiddling thumbs until the vsync arrives. */ +#define DEFAULT_MODULE "roto32" +#define DEFAULT_VIDEO "drm" extern fb_ops_t drm_fb_ops; @@ -48,16 +51,18 @@ static rototiller_module_t *modules[] = { }; -static void module_select(int *module) +rototiller_module_t * module_lookup(const char *name) { - int i; + unsigned i; + + assert(name); - printf("\nModules\n"); for (i = 0; i < nelems(modules); i++) { - printf(" %i: %s - %s\n", i, modules[i]->name, modules[i]->description); + if (!strcasecmp(name, modules[i]->name)) + return modules[i]; } - ask_num(module, nelems(modules) - 1, "Select module", 0); + return NULL; } @@ -125,33 +130,229 @@ int parse_argv(int argc, const char *argv[], argv_t *res_args) return 0; } +typedef struct setup_t { + settings_t *module; + settings_t *video; +} setup_t; + +/* FIXME: this is unnecessarily copy-pasta, i think modules should just be made + * more generic to encompass the setting up uniformly, then basically + * subclass the video backend vs. renderer stuff. + */ + +/* select video backend if not yet selected, then setup the selected backend. */ +static int setup_video(settings_t *settings, setting_desc_t **next_setting) +{ + const char *video; + + /* XXX: there's only one option currently, so this is simple */ + video = settings_get_key(settings, 0); + if (!video) { + setting_desc_t *desc; + const char *values[] = { + "drm", + NULL, + }; + + desc = setting_desc_new("Video Backend", + NULL, + "drm", + DEFAULT_VIDEO, + values, + NULL); + if (!desc) + return -ENOMEM; + + *next_setting = desc; + + return 1; + } + + /* XXX: this is temporarily simply restricted to drm */ + if (strcmp(video, "drm")) + return -EINVAL; + + return drm_fb_ops.setup(settings, next_setting); +} + +/* select module if not yet selected, then setup the module. */ +static int setup_module(settings_t *settings, setting_desc_t **next_setting) +{ + rototiller_module_t *module; + const char *name; + + name = settings_get_key(settings, 0); + if (!name) { + const char *values[nelems(modules) + 1] = {}; + const char *annotations[nelems(modules) + 1] = {}; + setting_desc_t *desc; + unsigned i; + + for (i = 0; i < nelems(modules); i++) { + values[i] = modules[i]->name; + annotations[i] = modules[i]->description; + } + + desc = setting_desc_new("Renderer Module", + NULL, + "[a-zA-Z0-9]+", + DEFAULT_MODULE, + values, + annotations); + if (!desc) + return -ENOMEM; + + *next_setting = desc; + + return 1; + } + + module = module_lookup(name); + if (!module) + return -EINVAL; + + /* TODO: here's where the module-specific settings would get hooked */ + + return 0; +} + +/* 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 */ +static int setup_from_args(argv_t *args, int defaults, setup_t *res_setup) +{ + int r, changes = 0; + setup_t setup; + + setup.module = settings_new(args->module); + if (!setup.module) + return -ENOMEM; + + setup.video = settings_new(args->video); + if (!setup.video) { + settings_free(setup.module); + + return -ENOMEM; + } + + r = setup_interactively(setup.module, setup_module, defaults); + if (r < 0) { + settings_free(setup.module); + settings_free(setup.video); + + return r; + } + + if (r) + changes = 1; + + r = setup_interactively(setup.video, setup_video, defaults); + if (r < 0) { + settings_free(setup.module); + settings_free(setup.video); + + return r; + } + + if (r) + changes = 1; + + *res_setup = setup; + + return changes; +} + + +static int print_setup_as_args(setup_t *setup) +{ + char *module_args, *video_args; + char buf[64]; + int r; + + module_args = settings_as_arg(setup->module); + if (!module_args) { + r = -ENOMEM; + + goto _out; + } + + video_args = settings_as_arg(setup->video); + if (!video_args) { + r = -ENOMEM; + + goto _out_module; + } + r = printf("\nConfigured settings as flags:\n --module=%s --video=%s\n\nPress enter to continue...\n", + module_args, + video_args); + + if (r < 0) + goto _out_video; + + (void) fgets(buf, sizeof(buf), stdin); + +_out_video: + free(video_args); +_out_module: + free(module_args); +_out: + return r; +} + + +static int print_help(void) +{ + return printf( + "Run without any flags or partial settings for interactive mode.\n" + "\n" + "Supported flags:\n" + " --defaults use defaults for unspecified settings\n" + " --help this help\n" + " --module= module settings\n" + " --video= video settings\n" + ); +} + + +/* When run with partial/no arguments, if stdin is a tty, enter an interactive setup. + * If stdin is not a tty, or if --defaults is supplied in argv, default settings are used. + * If any changes to the settings occur in the course of execution, either interactively or + * throught --defaults, then print out the explicit CLI invocation usable for reproducing + * the invocation. + */ int main(int argc, const char *argv[]) { - int drm_fd; - drmModeModeInfoPtr drm_mode; - uint32_t drm_crtc_id; - uint32_t drm_connector_id; + argv_t args = {}; + setup_t setup = {}; + void *context = NULL; + rototiller_module_t *module; threads_t *threads; - int module; fb_t *fb; - void *context = NULL; - drm_fb_t *drm_fb; + int r; + + exit_if(parse_argv(argc, argv, &args) < 0, + "unable to process arguments"); + + if (args.help) + return print_help() < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + exit_if((r = setup_from_args(&args, args.defaults, &setup)) < 0, + "unable to setup"); - drm_setup(&drm_fd, &drm_crtc_id, &drm_connector_id, &drm_mode); - module_select(&module); + exit_if(r && print_setup_as_args(&setup) < 0, + "unable to print setup"); - pexit_if(!(drm_fb = drm_fb_new(drm_fd, drm_crtc_id, &drm_connector_id, 1, drm_mode)), - "unable to create drm fb backend"); + exit_if(!(module = module_lookup(settings_get_key(setup.module, 0))), + "unable to lookup module from settings \"%s\"", settings_get_key(setup.module, 0)); - pexit_if(!(fb = fb_new(&drm_fb_ops, drm_fb, NUM_FB_PAGES)), - "unable to create fb frontend"); + exit_if(!(fb = fb_new(&drm_fb_ops, setup.video, NUM_FB_PAGES)), + "unable to create fb"); - pexit_if(!fps_setup(), + exit_if(!fps_setup(), "unable to setup fps counter"); - exit_if(modules[module]->create_context && - !(context = modules[module]->create_context()), + exit_if(module->create_context && + !(context = module->create_context()), "unable to create module context"); pexit_if(!(threads = threads_create()), @@ -163,17 +364,16 @@ int main(int argc, const char *argv[]) fps_print(fb); page = fb_page_get(fb); - module_render_page(modules[module], context, threads, page); + module_render_page(module, context, threads, page); fb_page_put(fb, page); } threads_destroy(threads); if (context) - modules[module]->destroy_context(context); + module->destroy_context(context); fb_free(fb); - close(drm_fd); return EXIT_SUCCESS; } |