Пример #1
0
/*
 * This is set-up to mimic the behavior of previous arcan
 * version as much as possible. For other, more controlled settings,
 * this is a good function to replace.
 */
void arcan_set_namespace_defaults()
{
	char* tmp = NULL;

/*
 * use environment variables as hard overrides
 */
	for (int i = 0; i < sizeof( envvs ) / sizeof( envvs[0] ); i++){
		const char* tmp = getenv(envvs[i]);
		arcan_override_namespace(tmp, 1 << i);
	}

/*
 * legacy mapping from the < 0.5 days
 */

	arcan_softoverride_namespace(tmp = binpath_unix(), RESOURCE_SYS_BINS);
	free(tmp);
	arcan_softoverride_namespace(tmp = libpath_unix(), RESOURCE_SYS_LIBS);
	free(tmp);

	char* respath = unix_find("resources");

	if (!respath)
		respath = arcan_expand_resource("", RESOURCE_APPL_SHARED);

	if (respath){
		size_t len = strlen(respath);
		char debug_dir[ len + sizeof("/logs") ];
		char font_dir[ len + sizeof("/fonts") ];

		snprintf(debug_dir, sizeof(debug_dir), "%s/logs", respath);
		snprintf(font_dir, sizeof(font_dir), "%s/fonts", respath);

		arcan_softoverride_namespace(respath, RESOURCE_APPL_SHARED);
		arcan_softoverride_namespace(debug_dir, RESOURCE_SYS_DEBUG);
		arcan_softoverride_namespace(respath, RESOURCE_APPL_STATE);
		arcan_softoverride_namespace(font_dir, RESOURCE_SYS_FONT);
		arcan_mem_free(respath);
	}

	char* scrpath = unix_find("appl");
	if (!scrpath)
		scrpath = unix_find("themes");

	if (scrpath){
		arcan_softoverride_namespace(scrpath, RESOURCE_SYS_APPLBASE);
		arcan_softoverride_namespace(scrpath, RESOURCE_SYS_APPLSTORE);
		arcan_mem_free(scrpath);
	}

	tmp = arcan_expand_resource("", RESOURCE_SYS_APPLSTATE);
	if (!tmp){
		tmp = arcan_expand_resource("savestates", RESOURCE_APPL_SHARED);
		if (tmp)
			arcan_override_namespace(tmp, RESOURCE_SYS_APPLSTATE);
	}

	arcan_mem_free(tmp);
}
Пример #2
0
void agp_empty_vstore(struct storage_info_t* vs, size_t w, size_t h)
{
	size_t sz = w * h * sizeof(av_pixel);
	vs->vinf.text.s_raw = sz;

/* this is to allow an override of s_fmt and still handle reset */
	if (vs->vinf.text.s_fmt == 0)
		vs->vinf.text.s_fmt = GL_PIXEL_FORMAT;
	if (vs->vinf.text.d_fmt == 0)
		vs->vinf.text.d_fmt = GL_STORE_PIXEL_FORMAT;

	vs->vinf.text.raw = arcan_alloc_mem(
		vs->vinf.text.s_raw,
		ARCAN_MEM_VBUFFER, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_PAGE
	);
	vs->w = w;
	vs->h = h;
	vs->bpp = sizeof(av_pixel);
	vs->txmapped = TXSTATE_TEX2D;

	agp_update_vstore(vs, true);

	arcan_mem_free(vs->vinf.text.raw);
	vs->vinf.text.raw = 0;
	vs->vinf.text.s_raw = 0;
}
Пример #3
0
void agp_resize_rendertarget(
	struct agp_rendertarget* tgt, size_t neww, size_t newh)
{
	if (!tgt || !tgt->store){
		arcan_warning("attempted resize on broken rendertarget\n");
		return;
	}

/* same dimensions, no need to resize */
	if (tgt->store->w == neww && tgt->store->h == newh)
		return;

	struct storage_info_t* os = tgt->store;

/* we inplace- modify, want the refcounter intact */
	agp_null_vstore(os);
	arcan_mem_free(os->vinf.text.raw);
	os->vinf.text.raw = NULL;
	os->vinf.text.s_raw = 0;
	agp_empty_vstore(os, neww, newh);

	glDeleteFramebuffers(1,&tgt->fbo);
	glDeleteRenderbuffers(1,&tgt->depth);
	tgt->fbo = GL_NONE;
	tgt->depth = GL_NONE;
	alloc_fbo(tgt, tgt->mode);
}
Пример #4
0
unsigned arcan_glob(char* basename, enum arcan_namespaces space,
	void (*cb)(char*, void*), void* tag)
{
	unsigned count = 0;
	if (!basename || verify_traverse(basename) == NULL)
		return 0;

	for (int i = 1; i <= RESOURCE_SYS_ENDM; i <<= 1){
		if ((space & i) == 0)
			continue;

		char* path = arcan_expand_resource(basename, i);
		WIN32_FIND_DATA finddata;
		HANDLE findh = FindFirstFile(path, &finddata);
		if (findh != INVALID_HANDLE_VALUE)
			do{
				if (!finddata.cFileName || strcmp(finddata.cFileName, "..") == 0 ||
					strcmp(finddata.cFileName, ".") == 0)
					continue;

				cb(finddata.cFileName, tag);

				count++;
			} while (FindNextFile(findh, &finddata));

		FindClose(findh);
		arcan_mem_free(path);
	}

	return count;
}
Пример #5
0
bool arcan_event_blacklisted(const char* idstr)
{
/* idstr comes from a trusted context, won't exceed stack size */
	char buf[strlen(idstr) + sizeof("bl_")];
	snprintf(buf, COUNT_OF(buf), "bl_%s", idstr);
	char* res = arcan_db_appl_val(dbhandle, ARCAN_TBL, "bl_");
	bool rv = res && strcmp(res, "block") == 0;
	arcan_mem_free(res);
	return rv;
}
Пример #6
0
static void erase_store(struct storage_info_t* os)
{
	if (!os)
		return;

	agp_null_vstore(os);
	arcan_mem_free(os->vinf.text.raw);
	os->vinf.text.raw = NULL;
	os->vinf.text.s_raw = 0;
}
Пример #7
0
void agp_dropenv(struct agp_fenv* env)
{
	if (!env || env->cookie != 0xfeedface){
		arcan_warning("agp_dropenv() - code issue: called on bad/broken fenv\n");
		return;
	}
	env->cookie = 0xdeadbeef;
	arcan_mem_free(env);
	if (agp_env() == env)
		agp_setenv(NULL);
}
Пример #8
0
void agp_drop_rendertarget(struct agp_rendertarget* tgt)
{
	if (!tgt)
		return;

	glDeleteFramebuffers(1,&tgt->fbo);
	glDeleteRenderbuffers(1,&tgt->depth);
	tgt->fbo = GL_NONE;
	tgt->depth = GL_NONE;
	arcan_mem_free(tgt);
}
Пример #9
0
void agp_drop_rendertarget(struct agp_rendertarget* tgt)
{
	if (!tgt)
		return;
	struct agp_fenv* env = agp_env();

	env->delete_framebuffers(1,&tgt->fbo);
	env->delete_renderbuffers(1,&tgt->depth);
	tgt->fbo = GL_NONE;
	tgt->depth = GL_NONE;
	arcan_mem_free(tgt);
}
Пример #10
0
static void alloc_buffer(struct storage_info_t* s)
{
	if (s->vinf.text.s_raw != s->w * s->h * sizeof(av_pixel)){
		arcan_mem_free(s->vinf.text.raw);
		s->vinf.text.raw = NULL;
	}

	if (!s->vinf.text.raw){
		s->vinf.text.s_raw = s->w * s->h * sizeof(av_pixel);
		s->vinf.text.raw = arcan_alloc_mem(s->vinf.text.s_raw,
			ARCAN_MEM_VBUFFER, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_PAGE);
	}
}
Пример #11
0
void agp_empty_vstore(struct storage_info_t* vs, size_t w, size_t h)
{
	size_t sz = w * h * sizeof(av_pixel);
	vs->vinf.text.s_raw = sz;
	vs->vinf.text.raw = arcan_alloc_mem(
		vs->vinf.text.s_raw,
		ARCAN_MEM_VBUFFER, ARCAN_MEM_BZERO, ARCAN_MEMALIGN_PAGE
	);
	vs->w = w;
	vs->h = h;
	vs->txmapped = TXSTATE_TEX2D;

	agp_update_vstore(vs, true);

	arcan_mem_free(vs->vinf.text.raw);
	vs->vinf.text.raw = 0;
	vs->vinf.text.s_raw = 0;
}
Пример #12
0
void agp_update_vstore(struct storage_info_t* s, bool copy)
{
	struct agp_fenv* env = agp_env();
	if (s->txmapped == TXSTATE_OFF)
		return;

	FLAG_DIRTY();

	if (!copy)
		env->bind_texture(GL_TEXTURE_2D, s->vinf.text.glid);
	else{
		if (GL_NONE == s->vinf.text.glid)
			env->gen_textures(1, &s->vinf.text.glid);

/* for the launch_resume and resize states, were we'd push a new
 * update	but have multiple references */
		if (s->refcount == 0)
			s->refcount = 1;

		env->bind_texture(GL_TEXTURE_2D, s->vinf.text.glid);
	}

	env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
		s->txu == ARCAN_VTEX_REPEAT ? GL_REPEAT : GL_CLAMP_TO_EDGE);
	env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
		s->txv == ARCAN_VTEX_REPEAT ? GL_REPEAT : GL_CLAMP_TO_EDGE);

	int filtermode = s->filtermode & (~ARCAN_VFILTER_MIPMAP);
	bool mipmap = s->filtermode & ARCAN_VFILTER_MIPMAP;

/*
 * Mipmapping still misses the option to manually define mipmap levels
 */
	if (copy){
#ifndef GL_GENERATE_MIPMAP
		if (mipmap)
			env->generate_mipmap(GL_TEXTURE_2D);
#else
			env->tex_param_i(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, mipmap);
#endif
	}

	switch (filtermode){
	case ARCAN_VFILTER_NONE:
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	break;

	case ARCAN_VFILTER_LINEAR:
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	break;

	case ARCAN_VFILTER_BILINEAR:
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	break;

	case ARCAN_VFILTER_TRILINEAR:
		if (mipmap){
			env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
				GL_LINEAR_MIPMAP_LINEAR);
			env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		}
		else {
			env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			env->tex_param_i(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		}
	break;
	}

	if (copy){
		env->pixel_storei(GL_UNPACK_ROW_LENGTH, 0);
		s->update_ts = arcan_timemillis();
		if (s->txmapped == TXSTATE_DEPTH)
			env->tex_image_2d(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, s->w, s->h, 0,
				GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
		else
			env->tex_image_2d(GL_TEXTURE_2D, 0,
				s->vinf.text.d_fmt ? s->vinf.text.d_fmt : GL_STORE_PIXEL_FORMAT,
				s->w, s->h, 0,
				s->vinf.text.s_fmt ? s->vinf.text.s_fmt : GL_PIXEL_FORMAT,
				GL_UNSIGNED_BYTE, s->vinf.text.raw
			);
	}

#ifndef HEADLESS_NOARCAN
	if (arcan_video_display.conservative){
		arcan_mem_free(s->vinf.text.raw);
		s->vinf.text.raw = NULL;
		s->vinf.text.s_raw = 0;
	}
#endif

	env->bind_texture(GL_TEXTURE_2D, 0);
}
Пример #13
0
void agp_update_vstore(struct storage_info_t* s, bool copy)
{
	if (s->txmapped == TXSTATE_OFF)
		return;

	FLAG_DIRTY();

	if (!copy)
		glBindTexture(GL_TEXTURE_2D, s->vinf.text.glid);
	else{
		glGenTextures(1, &s->vinf.text.glid);

/* for the launch_resume and resize states, were we'd push a new
 * update	but have multiple references */
		if (s->refcount == 0)
			s->refcount = 1;

		glBindTexture(GL_TEXTURE_2D, s->vinf.text.glid);
	}

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, s->txu == ARCAN_VTEX_REPEAT ?
		GL_REPEAT : GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, s->txv == ARCAN_VTEX_REPEAT ?
		GL_REPEAT : GL_CLAMP_TO_EDGE);

	int filtermode = s->filtermode & (~ARCAN_VFILTER_MIPMAP);
	bool mipmap = s->filtermode & ARCAN_VFILTER_MIPMAP;

/*
 * Mipmapping still misses the option to manually define mipmap levels
 */
	if (copy){
#ifndef GL_GENERATE_MIPMAP
		if (mipmap)
			glGenerateMipmap(GL_TEXTURE_2D);
#else
			glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, mipmap);
#endif
	}

	switch (filtermode){
	case ARCAN_VFILTER_NONE:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	break;

	case ARCAN_VFILTER_LINEAR:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	break;

	case ARCAN_VFILTER_BILINEAR:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	break;

	case ARCAN_VFILTER_TRILINEAR:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
			GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	break;
	}

	if (copy){
		s->update_ts = arcan_timemillis();
		if (s->txmapped == TXSTATE_DEPTH)
			glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, s->w, s->h, 0,
				GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
		else
			glTexImage2D(GL_TEXTURE_2D, 0, GL_PIXEL_FORMAT, s->w, s->h,
				0, GL_PIXEL_FORMAT, GL_UNSIGNED_BYTE, s->vinf.text.raw);
	}

#ifndef HEADLESS_NOARCAN
	if (arcan_video_display.conservative){
		arcan_mem_free(s->vinf.text.raw);
		s->vinf.text.raw = NULL;
		s->vinf.text.s_raw = 0;
	}
#endif

	glBindTexture(GL_TEXTURE_2D, 0);
}
Пример #14
0
int main(int argc, char* argv[])
{
	settings.in_monitor = getenv("ARCAN_MONITOR_FD") != NULL;
	bool windowed = false;
	bool fullscreen = false;
	bool conservative = false;
	bool nosound = false;

	unsigned char debuglevel = 0;

	int scalemode = ARCAN_VIMAGE_NOPOW2;
	int width = -1;
	int height = -1;

/* only used when monitor mode is activated, where we want some
 * of the global paths etc. accessible, but not *all* of them */
	char* monitor_arg = "LOG";

/*
 * if we crash in the Lua VM, switch to this app and have it
 * adopt our external connections
 */
	char* fallback = NULL;
	char* hookscript = NULL;
	char* dbfname = NULL;
	int ch;

	srand( time(0) );
/* VIDs all have a randomized base to provoke crashes in poorly written scripts,
 * only -g will make their base and sequence repeatable */

	while ((ch = getopt_long(argc, argv,
		"w:h:mx:y:fsW:d:Sq:a:p:b:B:M:O:t:H:g1:2:V", longopts, NULL)) >= 0){
	switch (ch) {
	case '?' :
		usage();
		exit(EXIT_SUCCESS);
	break;
	case 'w' : width = strtol(optarg, NULL, 10); break;
	case 'h' : height = strtol(optarg, NULL, 10); break;
	case 'm' : conservative = true; break;
	case 'f' : fullscreen = true; break;
	case 's' : windowed = true; break;
	case 'W' : platform_video_setsynch(optarg); break;
	case 'd' : dbfname = strdup(optarg); break;
	case 'S' : nosound = true; break;
	case 'q' : settings.timedump = strtol(optarg, NULL, 10); break;
	case 'p' : arcan_override_namespace(optarg, RESOURCE_APPL_SHARED); break;
	case 'b' : fallback = strdup(optarg); break;
	case 'V' : fprintf(stdout, "%s\nshmif-%" PRIu64"\nluaapi-%d:%d\n",
		ARCAN_BUILDVERSION, arcan_shmif_cookie(), LUAAPI_VERSION_MAJOR,
		LUAAPI_VERSION_MINOR
		);
		exit(EXIT_SUCCESS);
	break;
	case 'H' : hookscript = strdup( optarg ); break;
#ifndef _WIN32
	case 'M' : settings.monitor_counter = settings.monitor =
		abs( (int)strtol(optarg, NULL, 10) ); break;
	case 'O' : monitor_arg = strdup( optarg ); break;
#endif
	case 't' :
		arcan_override_namespace(optarg, RESOURCE_SYS_APPLBASE);
		arcan_override_namespace(optarg, RESOURCE_SYS_APPLSTORE);
	break;
	case 'B' :
		arcan_override_namespace(optarg, RESOURCE_SYS_BINS);
	break;
	case 'g' :
		debuglevel++;
		srand(0xdeadbeef);
		break;
	break;
	case '1' :
		stdout_redirected = true;
		if (freopen(optarg, "a", stdout) == NULL);
		break;
	case '2' :
		stderr_redirected = true;
		if (freopen(optarg, "a", stderr) == NULL);
		break;

	default:
		break;
	}
	}

	if (optind >= argc){
		arcan_warning("Couldn't start, missing 'applname' argument. \n"
			"Consult the manpage (man arcan) for additional details\n");
		usage();
		exit(EXIT_SUCCESS);
	}

/* probe system, load environment variables, ... */
	arcan_set_namespace_defaults();
	arcan_ffunc_initlut();
#ifdef DISABLE_FRAMESERVERS
	arcan_override_namespace("", RESOURCE_SYS_BINS);
#endif

	const char* err_msg;

	if (!arcan_verifyload_appl(argv[optind], &err_msg)){
		arcan_warning("arcan_verifyload_appl(), "
			"failed to load (%s), reason: %s.",
			argv[optind], err_msg);

		if (fallback){
			arcan_warning("trying to load fallback application (%s)\n", fallback);
			if (!arcan_verifyload_appl(fallback, &err_msg)){
				arcan_warning("fallback application failed to load (%s), giving up.\n",
					err_msg);
				goto error;
			}
		}
		else
			goto error;
	}

	if (!arcan_verify_namespaces(false)){
		arcan_warning("namespace verification failed, status:\n");
		goto error;
	}

	if (debuglevel > 1)
		arcan_verify_namespaces(true);

#ifndef _WIN32
/* pipe to file, socket or launch script based on monitor output,
 * format will be LUA tables with the exception of each cell ending with
 * #ENDSAMPLE . The block will be sampled, parsed and should return a table
 * pushed through the sample() function in the LUA space */
	if (settings.in_monitor){
		settings.mon_infd = strtol( getenv("ARCAN_MONITOR_FD"), NULL, 10);
	}
	else if (settings.monitor > 0){
		extern arcan_benchdata benchdata;
		benchdata.bench_enabled = true;

		if (strncmp(monitor_arg, "LOG:", 4) == 0){
			settings.mon_outf = fopen(&monitor_arg[4], "w+");
			if (NULL == settings.mon_outf)
				arcan_fatal("couldn't open log output (%s) for writing\n",
					monitor_arg[4]);
			fcntl(fileno(settings.mon_outf), F_SETFD, FD_CLOEXEC);
		}
		else {
			int pair[2];

			pid_t p1;
			if (pipe(pair) == 0)
				;

			if ( (p1 = fork()) == 0){
				close(pair[1]);

/* double-fork to get away from parent */
				if (fork() != 0)
					exit(EXIT_SUCCESS);

/*
 * set the descriptor of the inherited pipe as an envvariable,
 * this will have the program be launched with in_monitor set to true
 * the monitor args will then be ignored and appname replaced with
 * the monitorarg
 */
				char monfd_buf[8] = {0};
				snprintf(monfd_buf, 8, "%d", pair[0]);
				setenv("ARCAN_MONITOR_FD", monfd_buf, 1);
				argv[optind] = strdup(monitor_arg);

				execv(argv[0], argv);
				exit(EXIT_FAILURE);
			}
			else {
/* don't terminate just because the pipe gets broken (i.e. dead monitor) */
				close(pair[0]);
				settings.mon_outf = fdopen(pair[1], "w");
			 	signal(SIGPIPE, SIG_IGN);
			}
		}

		fullscreen = false;
	}
#else
	if (!stdout_redirected){
	}
#endif

/*
 * try to open the specified database,
 * if that fails, warn, try to create an empty
 * database and if that fails, give up.
 */
	if (!dbfname)
		dbfname = arcan_expand_resource("arcandb.sqlite", RESOURCE_APPL_SHARED);

	dbhandle = arcan_db_open(dbfname, arcan_appl_id());

	if (!dbhandle) {
		arcan_warning("Couldn't open database (requested: %s),"
			"trying to create a new one.\n", dbfname);
		FILE* fpek = fopen(dbfname, "w+");

/* case of non-write:able dbpath */
		if (!fpek){
			arcan_mem_free(dbfname);
			dbfname = arcan_expand_resource("arcandb.sqlite", RESOURCE_APPL_TEMP);
			if (!dbfname)
				goto error;

			fpek = fopen(dbfname, "w+");
		}

		if (fpek){
			fclose(fpek);
			dbhandle = arcan_db_open(dbfname, arcan_appl_id());
		}

		if (!dbhandle){
			arcan_warning("Couldn't create database, using in-mem fallback\n");
			dbhandle = arcan_db_open(":memory:", arcan_appl_id());
		}

		if (!dbhandle){
			arcan_warning("In memory db fallback failed, giving up\n");
			goto error;
		}
	}

/* either use previous explicit dimensions (if found and cached)
 * or revert to platform default or store last */
	if (-1 == width){
		char* dbw = arcan_db_appl_val(dbhandle, "arcan", "width");
		if (dbw){
			width = (uint16_t) strtoul(dbw, NULL, 10);
			arcan_mem_free(dbw);
		}
		else
			width = 0;
	}
	else{
		char buf[6] = {0};
		snprintf(buf, sizeof(buf), "%d", width);
		arcan_db_appl_kv(dbhandle, "arcan", "width", buf);
	}

	if (-1 == height){
		char* dbh = arcan_db_appl_val(dbhandle, "arcan", "height");
		if (dbh){
			height = (uint16_t) strtoul(dbh, NULL, 10);
			arcan_mem_free(dbh);
		}
		else
			height = 0;
	}
	else{
		char buf[6] = {0};
		snprintf(buf, sizeof(buf), "%d", height);
		arcan_db_appl_kv(dbhandle, "arcan", "height", buf);
	}

	arcan_video_default_scalemode(scalemode);

	if (windowed)
		fullscreen = false;

/* grab video, (necessary) */
	if (arcan_video_init(width, height, 32, fullscreen, windowed,
		conservative, arcan_appl_id()) != ARCAN_OK){
		arcan_fatal("Error; Couldn't initialize video system,"
			"try other windowing options (-f, -w, ...)\n");
	}

/* defined in warning.c for arcan_fatal, we avoid the use of an
 * atexit() as this can be routed through abort() or similar
 * functions but some video platforms are extremely volatile
 * if we don't initiate a shutdown (egl-dri for one) */
	extern void(*arcan_fatal_hook)(void);
	arcan_fatal_hook = arcan_video_shutdown;

	errno = 0;
/* grab audio, (possible to live without) */
	if (ARCAN_OK != arcan_audio_setup(nosound))
		arcan_warning("Warning: No audio devices could be found.\n");

	arcan_math_init();

/* setup device polling, cleanup, ... */
	arcan_evctx* def = arcan_event_defaultctx();
	arcan_event_init( def );

#ifdef ARCAN_LED
	arcan_led_init();
#endif

/*
 * system integration note here, this could essentially provide
 * a side-channel into debugdata as the path can be expanded to
 * the debug directory, thus a compromised frameserver could possibly
 * leak crash-dumps etc. outside the sandbox. If this is a concern,
 * change this behavior or define a different logpath in the env.
 */
	if (getenv("ARCAN_FRAMESERVER_LOGDIR") == NULL){
		char* lpath = arcan_expand_resource("", RESOURCE_SYS_DEBUG);
		setenv("ARCAN_FRAMESERVER_LOGDIR", lpath, 1);
		arcan_mem_free(lpath);
	}

	if (hookscript){
		char* tmphook = arcan_expand_resource(hookscript, RESOURCE_APPL_SHARED);
		free(hookscript);
		hookscript = NULL;

		if (tmphook){
			data_source src = arcan_open_resource(tmphook);
			if (src.fd != BADFD){
				map_region reg = arcan_map_resource(&src, false);

				if (reg.ptr){
					hookscript = strdup(reg.ptr);
					arcan_release_map(reg);
				}

				arcan_release_resource(&src);
			}

			free(tmphook);
		}
	}
#ifndef _WIN32
	system_page_size = sysconf(_SC_PAGE_SIZE);
#endif

/*
 * fallback implementation resides here and a little further down
 * in the "if adopt" block. Use verifyload to reconfigure application
 * namespace and scripts to run, then recoverexternal will cleanup
 * audio/video/event and invoke adopt() in the script
 */
	bool adopt = false, in_recover = false;
	int jumpcode = setjmp(arcanmain_recover_state);

	if (jumpcode == 1){
		arcan_db_close(&dbhandle);

		dbhandle = arcan_db_open(dbfname, arcan_appl_id());
		if (!dbhandle)
			goto error;

		adopt = true;
	}
	else if (jumpcode == 2){
		if (in_recover){
			arcan_warning("Double-Failure (main appl + adopt appl), giving up.\n");
			goto error;
		}

		if (!fallback){
			arcan_warning("Lua VM instance failed and no "
				"fallback defined, giving up.\n");
			goto error;
		}

		const char* errmsg;
		arcan_lua_shutdown(settings.lua);
		if (!arcan_verifyload_appl(fallback, &errmsg)){
			arcan_warning("Lua VM error fallback, failure loading (%s), reason: %s\n",
				fallback, errmsg);
			goto error;
		}

		if (!arcan_verify_namespaces(false))
			goto error;

		in_recover = true;
		adopt = true;
	}

/* setup VM, map arguments and possible overrides */
	settings.lua = arcan_lua_alloc();
	arcan_lua_mapfunctions(settings.lua, debuglevel);

	bool inp_file;
	const char* inp = arcan_appl_basesource(&inp_file);
	if (!inp){
		arcan_warning("main(), No main script found for (%s)\n", arcan_appl_id());
		goto error;
	}

	char* msg = arcan_lua_main(settings.lua, inp, inp_file);
	if (msg != NULL){
#ifdef ARCAN_LUA_NOCOLOR
		arcan_warning("\nParsing error in %s:\n%s\n", arcan_appl_id(), msg);
#else
		arcan_warning("\n\x1b[1mParsing error in (\x1b[33m%s\x1b[39m):\n"
			"\x1b[35m%s\x1b[22m\x1b[39m\n\n", arcan_appl_id(), msg);
#endif
		goto error;
	}
	free(msg);

/* entry point follows the name of the appl,
 * hand over execution and begin event loop */
	if (argc > optind)
		arcan_lua_pushargv(settings.lua, argv + optind + 1);

	if (!arcan_lua_callvoidfun(settings.lua, "", false))
		arcan_fatal("couldn't load appl, missing %s function\n", arcan_appl_id() ?
			arcan_appl_id() : "");

	if (hookscript)
		arcan_lua_dostring(settings.lua, hookscript);

	if (adopt){
		int saved, truncated;
		arcan_video_recoverexternal(false, &saved, &truncated,
			arcan_lua_adopt, settings.lua);
		arcan_warning("switching applications, %d adopted.\n", saved);
	}

	arcan_evctx* evctx = arcan_event_defaultctx();
	bool done = false;
	int exit_code = EXIT_SUCCESS;
	arcan_event ev;

	while (!done) {
/* these can populate event queue, but only to a certain limit */
		arcan_video_pollfeed();
		arcan_audio_refresh();

		float frag = arcan_event_process(evctx, on_clock_pulse);
		while (1 == arcan_event_poll(evctx, &ev)){
			switch (ev.category){
			case EVENT_VIDEO:
				if (ev.vid.kind == EVENT_VIDEO_EXPIRE)
					arcan_video_deleteobject(ev.vid.source);
			break;

/* this event category is never propagated to the scripting engine itself */
			case EVENT_SYSTEM:
				if (ev.sys.kind == EVENT_SYSTEM_EXIT){
					exit_code = ev.sys.errcode;
					done = true;
					goto out;
				}
				goto error;
			default:
			break;
			}

			arcan_lua_pushevent(settings.lua, &ev);
		}

		platform_video_synch(settings.tick_count, frag, preframe, postframe);
	}

out:
	free(hookscript);
	arcan_lua_callvoidfun(settings.lua, "shutdown", false);
#ifdef ARCAN_LED
	arcan_led_shutdown();
#endif
	arcan_video_shutdown();
	arcan_event_deinit(arcan_event_defaultctx());
	arcan_mem_free(dbfname);
	if (dbhandle)
		arcan_db_close(&dbhandle);

	return exit_code;

error:
	if (debuglevel > 1){
		arcan_warning("fatal: main loop failed, arguments: \n");
		for (size_t i = 0; i < argc; i++)
			arcan_warning("%s ", argv[i]);
		arcan_warning("\n\n");
	}

	arcan_mem_free(dbfname);
	arcan_video_shutdown();
	arcan_event_deinit(arcan_event_defaultctx());

/* for cases where we have a broken namespace, also dump
 * the current namespace state as that might help troubleshoot */
	arcan_verify_namespaces(true);

	return EXIT_FAILURE;
}