diff options
Diffstat (limited to 'src')
| -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;  }  | 
