Exemplo n.º 1
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);
}
Exemplo n.º 2
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;

	erase_store(tgt->store);
	erase_store(tgt->store_back);

	struct agp_fenv* env = agp_env();

	agp_empty_vstore(tgt->store, neww, newh);
	if (tgt->store_back)
		agp_empty_vstore(tgt->store_back, neww, newh);

/* we inplace- modify, want the refcounter intact */
	env->delete_framebuffers(1,&tgt->fbo);
	env->delete_renderbuffers(1,&tgt->depth);
	tgt->fbo = GL_NONE;
	tgt->depth = GL_NONE;
	alloc_fbo(tgt, false);
}
Exemplo n.º 3
0
/*
 * If the shmpage integrity is somehow compromised,
 * if semaphore use is out of order etc.
 */
static void pull_killswitch(arcan_evctx* ctx)
{
	arcan_frameserver* ks = (arcan_frameserver*) ctx->synch.killswitch;
	arcan_sem_post(ctx->synch.handle);
	arcan_warning("inconsistency while processing "
		"shmpage events, pulling killswitch.\n");
	arcan_frameserver_free(ks);
	ctx->synch.killswitch = NULL;
}
Exemplo n.º 4
0
void arcan_event_dump(struct arcan_evctx* ctx)
{
	unsigned front = *ctx->front;
	size_t count = 0;

	while (front != *ctx->back){
		arcan_warning("slot: %d, category: %d, kind: %d\n",
			count, ctx->eventbuf[front].io.kind, ctx->eventbuf[front].category);
		front = (front + 1) % ctx->eventbuf_sz;
	}
}
Exemplo n.º 5
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);
}
Exemplo n.º 6
0
Arquivo: video.c Projeto: mewbak/arcan
void platform_video_setsynch(const char* arg)
{
	int ind = 0;

	while(synchopts[ind]){
		if (strcmp(synchopts[ind], arg) == 0){
			synchopt = (ind > 0 ? ind / 2 : ind);
			arcan_warning("synchronisation strategy set to (%s)\n", synchopts[ind]);
			break;
		}

		ind += 2;
	}
}
Exemplo n.º 7
0
bool switch_appl(const char* appname)
{
	arcan_video_shutdown();
	arcan_audio_shutdown();
	arcan_event_deinit(arcan_event_defaultctx());
	arcan_db_close(&dbhandle);

	const char* err_msg;
	if (!arcan_verifyload_appl(appname, &err_msg)){
		arcan_warning("(verifyload) in switch app "
			"failed, reason: %s\n", err_msg);
		return false;
	}

	return true;
}
Exemplo n.º 8
0
Arquivo: video.c Projeto: mewbak/arcan
bool platform_video_map_display(arcan_vobj_id id,
	platform_display_id disp, enum blitting_hint hint)
{
	if (disp != 0)
		return false;

	arcan_vobject* vobj = arcan_video_getobject(id);
	bool isrt = arcan_vint_findrt(vobj) != NULL;

	if (vobj && vobj->vstore->txmapped != TXSTATE_TEX2D){
		arcan_warning("platform_video_map_display(), attempted to map a "
			"video object with an invalid backing store");
		return false;
	}

/*
 * The constant problem of what are we drawing and how are we drawing it
 * (rts were initially used for 3d models, vobjs were drawin with inverted ys
 * and world normally etc. a huge mess)
 */
	size_t drawx = 0, drawy = 0;
	if (isrt){
		arcan_vint_applyhint(vobj, hint, vobj->txcos ? vobj->txcos :
			arcan_video_display.mirror_txcos, txcos,
			&drawx, &drawy,
			&d_width, &d_height,
			&blackframes);
	}
/* direct VOBJ mapping, prepared for indirect drawying so flip yhint */
	else {
		arcan_vint_applyhint(vobj,
		(hint & HINT_YFLIP) ? (hint & (~HINT_YFLIP)) : (hint | HINT_YFLIP),
		vobj->txcos ? vobj->txcos : arcan_video_display.default_txcos, txcos,
		&drawx, &drawy,
		&d_width, &d_height,
		&blackframes);
	}

	out_vid = id;
	return true;
}
Exemplo n.º 9
0
Arquivo: paths.c Projeto: jeoerl/arcan
static char* rep_str(char* instr)
{
	char* beg = strchr(instr, '[');
	if (!beg)
		return instr;

	char* end = strchr(beg+1, ']');

	for (size_t i = 0; i < sizeof(envvs)/sizeof(envvs[0]); i++){
		if (end)
			*end = '\0';

		if (strcmp(envvs[i], beg+1) != 0)
			continue;

		char* exp = arcan_expand_resource("", 1 << i);

		if (!exp)
			goto fail;

		if (!end){
			*beg = '\0';
			char* newstr = alloc_cat(instr, exp);
			free(instr);
			return newstr;
		}
		else{
			*beg = '\0';
			*end = '\0';
			char* newstr = alloc_cat(instr, exp);
			free(instr);
			char* resstr = alloc_cat(newstr, end+1);
			free(newstr);
			return rep_str(resstr);
		}
	}

fail:
	arcan_warning("expand failed, no match for supplied string (%s)\n", beg+1);
	return instr;
}
Exemplo n.º 10
0
/*
 * current several namespaces are (legacy) specified relative
 * to the old resources namespace, since those are expanded in
 * set_namespace_defaults where the command-line switch isn't
 * available, we have to generate these dependent namespaces
 * overrides as well.
 */
static void override_resspaces(const char* respath)
{
	size_t len = strlen(respath);
	if (len == 0 || !arcan_isdir(respath)){
		arcan_warning("-p argument ignored, invalid path specified.");
		return;
	}

	char debug_dir[ len + sizeof("/logs") ];
	char state_dir[ len + sizeof("/savestates") ];
	char font_dir[ len + sizeof("/fonts") ];

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

	arcan_override_namespace(respath, RESOURCE_APPL_SHARED);
	arcan_override_namespace(debug_dir, RESOURCE_SYS_DEBUG);
	arcan_override_namespace(state_dir, RESOURCE_APPL_STATE);
	arcan_override_namespace(font_dir, RESOURCE_SYS_FONT);
}
Exemplo n.º 11
0
static bool alloc_fbo(struct agp_rendertarget* dst, bool retry)
{
	struct agp_fenv* env = agp_env();
	env->gen_framebuffers(1, &dst->fbo);
	int mode = dst->mode & (~RENDERTARGET_DOUBLEBUFFER);

/* need both stencil and depth buffer, but we don't need the data from them */
	env->bind_framebuffer(GL_FRAMEBUFFER, dst->fbo);

	if (mode > RENDERTARGET_DEPTH)
	{
		env->framebuffer_texture_2d(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
			GL_TEXTURE_2D, dst->store->vinf.text.glid, 0);

/* need a Z buffer in the offscreen rendering but don't want
 * bo store it, so setup a renderbuffer */
		if (mode > RENDERTARGET_COLOR){
			env->gen_renderbuffers(1, &dst->depth);

/* could use GL_DEPTH_COMPONENT only if we'd know that there
 * wouldn't be any clipping in the active rendertarget */
			if (!retry){
				env->bind_renderbuffer(GL_RENDERBUFFER, dst->depth);
				env->renderbuffer_storage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
					dst->store->w, dst->store->h);
				env->bind_renderbuffer(GL_RENDERBUFFER, 0);
				env->framebuffer_renderbuffer(GL_FRAMEBUFFER,
					GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, dst->depth);
			}
		}
	}
	else {
/* DEPTH buffer only (shadowmapping, ...) convert the storage to
 * contain a depth texture */
		size_t w = dst->store->w;
		size_t h = dst->store->h;

		agp_drop_vstore(dst->store);

		struct storage_info_t* store = dst->store;

		memset(store, '\0', sizeof(struct storage_info_t));

		store->txmapped   = TXSTATE_DEPTH;
		store->txu        = ARCAN_VTEX_CLAMP;
		store->txv        = ARCAN_VTEX_CLAMP;
		store->scale      = ARCAN_VIMAGE_NOPOW2;
		store->imageproc  = IMAGEPROC_NORMAL;
		store->filtermode = ARCAN_VFILTER_NONE;
		store->refcount   = 1;
		store->w = w;
		store->h = h;

/* generate ID etc. special path for TXSTATE_DEPTH */
		agp_update_vstore(store, true);

		env->draw_buffer(GL_NONE);
		env->read_buffer(GL_NONE);

		env->framebuffer_texture_2d(GL_FRAMEBUFFER,
			GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, store->vinf.text.glid, 0);
	}

/* basic error handling / status checking
 * may be possible that we should cache this in the
 * rendertarget and only call when / if something changes as
 * it's not certain that drivers won't stall the pipeline on this */
	GLenum status = env->check_framebuffer(GL_FRAMEBUFFER);
	if (status != GL_FRAMEBUFFER_COMPLETE){
		arcan_warning("FBO support broken, couldn't create basic FBO:\n");
		switch(status){
		case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
			if (!retry){
				arcan_warning("\t Incomplete Attachment, attempting "
					"simple framebuffer, this will likely break 3D and complex"
					"clipping operations.\n");
				return alloc_fbo(dst, true);
			}
			else
				arcan_warning("\t Simple attachement broke as well "
					"likely driver issue.\n");
		break;

#ifdef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
		case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
			arcan_warning("\t Not all attached buffers have "
				"the same dimensions.\n");
		break;
#endif

		case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
			arcan_warning("\t One or several FBO attachment points are missing.\n");
		break;

		case GL_FRAMEBUFFER_UNSUPPORTED:
			arcan_warning("\t Request formats combination unsupported.\n");
		break;
		}

		if (dst->fbo != GL_NONE)
			env->delete_framebuffers(1,&dst->fbo);
		if (dst->depth != GL_NONE)
			env->delete_renderbuffers(1,&dst->depth);

		dst->fbo = dst->depth = GL_NONE;
		return false;
	}

	env->bind_framebuffer(GL_FRAMEBUFFER, 0);
	return true;
}
Exemplo n.º 12
0
bool arcan_verify_namespaces(bool report)
{
	bool working = true;

	if (report)
		arcan_warning("--- Verifying Namespaces: ---\n");

/* 1. check namespace mapping for holes */
	for (int i = 0; i < sizeof(
		namespaces.paths) / sizeof(namespaces.paths[0]); i++){
			if (namespaces.paths[i] == NULL){
				if (i != (int)log2(RESOURCE_SYS_LIBS)){
					working = false;
					if (report)
						arcan_warning("%s -- broken\n", lbls[i]);
					continue;
				}
			}

		if (report)
			arcan_warning("%s -- OK (%s)\n", lbls[i], namespaces.paths[i]);
	}

	if (report)
		arcan_warning("--- Namespace Verification Completed ---\n");

/* 2. missing; check permissions for each mounted space, i.e. we should be able
 * to write to state, we should be able to write to appl temporary etc.  also
 * check disk space for possible warning conditions (these should likely also
 * be emitted as system events)
 */

	if (working){
		char* toktmp = strdup(FRAMESERVER_MODESTRING);

/* modestring is static, atypestr can only be reduced in bytes used */
		if (!atypestr)
			atypestr = strdup(FRAMESERVER_MODESTRING);

		char* tokctx, (* tok) = strtok_r(toktmp, " ", &tokctx);
		if (tok && atypestr){
			char* base = arcan_expand_resource("", RESOURCE_SYS_BINS);
			size_t baselen = strlen(base);

/* fix for specialized "do we have default arcan_frameserver? then compact to
 * afsrv_ for archetype prefix" mode */
			size_t sfxlen = sizeof("arcan_frameserver") - 1;
			if (baselen >= sfxlen){
				if (strcmp(&base[baselen - sfxlen], "arcan_frameserver") == 0){
					const char* sfx = "afsrv";
					memcpy(&base[baselen - sfxlen], sfx, sizeof("afsrv"));
				}
			}

/* could / should do a more rigorous test of the corresponding afsrv, e.g.
 * executable, permission and linked shmif version */
			atypestr[0] = '\0';
			bool first = true;
			do{
				char* fn;
				char exp[2 + baselen + strlen(tok)];
				snprintf(exp, sizeof(exp), "%s_%s", base, tok);
				if (arcan_isfile(exp)){
					if (!first){
						strcat(atypestr, " ");
					}
					strcat(atypestr, tok);
					first = false;
				}
			} while ((tok = strtok_r(NULL, " ", &tokctx)));

			free(base);
		}
		free(toktmp);
	}

	return working;
}
Exemplo n.º 13
0
int main(int argc, char* argv[])
{
	bool windowed     = false;
	bool fullscreen   = false;
	bool conservative = false;
	bool nosound      = false;
	bool in_monitor   = getenv("ARCAN_MONITOR_FD") != NULL;
	bool waitsleep    = true;

	unsigned char debuglevel = 0;

	int scalemode = ARCAN_VIMAGE_NOPOW2;
	int width     = 640;
	int height    = 480;
	int winx      = -1;
	int winy      = -1;
	int timedump  = 0;
	float vfalign = 0.6;
	
/* only used when monitor mode is activated, where we want some 
 * of the global paths etc. accessible, but not *all* of them */
	FILE* monitor_outf    = NULL;
	int monitor           = 0;
	int monitor_infd      = -1;
	char* monitor_arg     = "LOG";

	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:x:y:?fvVmisp:q:"
		"t:M:O:o:l:a:d:F:1:2:gr:S", longopts, NULL)) >= 0){
	switch (ch) {
	case '?' :
		usage();
		exit(1);
	break;
	case 'w' : width = strtol(optarg, NULL, 10); break;
	case 'h' : height = strtol(optarg, NULL, 10); break;
	case 'm' : conservative = true; break;
	case 'x' : winx = strtol(optarg, NULL, 10); break;
	case 'y' : winy = strtol(optarg, NULL, 10); break;
	case 'F' : vfalign = strtof(optarg, NULL); break;
	case 'f' : fullscreen = true; break;
	case 's' : windowed = true; break;
	case 'l' : arcan_libpath = strdup(optarg); break;
	case 'd' : dbfname = strdup(optarg); break;
	case 'S' : nosound = true; break;
	case 'q' : timedump = strtol(optarg, NULL, 10); break;
	case 'a' : arcan_video_display.msasamples = strtol(optarg, NULL, 10); break;
	case 'v' : arcan_video_display.vsync = false; break;
	case 'V' : waitsleep = false; break;
	case 'p' : arcan_resourcepath = strdup(optarg); break;
#ifndef _WIN32
	case 'M' : monitor = abs( strtol(optarg, NULL, 10) ); break;
	case 'O' : monitor_arg = strdup( optarg ); break; 
#endif
	case 't' : arcan_themepath = strdup(optarg); break;
	case 'o' : arcan_binpath = strdup(optarg); break;
	case 'g' :
		debuglevel++;
		srand(0xdeadbeef);
		break;
	case 'r' :
		scalemode = strtol(optarg, NULL, 10);
		printf("scalemode: %d\n", scalemode);
		if (scalemode != ARCAN_VIMAGE_NOPOW2 && scalemode != 
			ARCAN_VIMAGE_SCALEPOW2 && scalemode){
			arcan_warning("Warning: main(), -r, invalid scalemode. Ignoring.\n");
			scalemode = ARCAN_VIMAGE_SCALEPOW2;
		}
	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 (arcan_setpaths() == false)
		goto error;

	if (check_theme(*(argv + optind)))
		arcan_themename = *(argv + optind);
	else if (check_theme("welcome"))
		arcan_themename = "welcome";
	else
		arcan_fatal("No theme found.\n");

	if (strcmp(arcan_themename, "arcan") == 0){
		arcan_fatal("Theme name 'arcan' is reserved\n");
	}

	if (vfalign > 1.0 || vfalign < 0.0){
		arcan_warning("Argument Error (-F, --vsync-falign): "
		"bad range specified (%f), reverting to default (0.6)\n", vfalign);
		vfalign = 0.6;
	}

#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 (in_monitor){
		monitor_infd = strtol( getenv("ARCAN_MONITOR_FD"), NULL, 10);
	 	signal(SIGPIPE, SIG_IGN);	
	}
	else if (monitor > 0){
		extern arcan_benchdata benchdata;
		benchdata.bench_enabled = true;

		if (strncmp(monitor_arg, "LOG:", 4) == 0){
			monitor_outf = fopen(&monitor_arg[4], "w+");	
			if (NULL == monitor_outf)
				arcan_fatal("couldn't open log output (%s) for writing\n", monitor_arg[4]);
			fcntl(fileno(monitor_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(0); 

/* 
 * 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 themename 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(1);
			}
			else {
/* don't terminate just because the pipe gets broken (i.e. dead monitor) */
				close(pair[0]);
				monitor_outf = fdopen(pair[1], "w");
			 	signal(SIGPIPE, SIG_IGN);	
			}
		}
		
		fullscreen = false;
	}
#endif

/* also used as restart point for switiching themes */
themeswitch:

/*
 * 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", true);

	dbhandle = arcan_db_open(dbfname, arcan_themename);

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

		if (!dbhandle){
			arcan_warning("Couldn't create database, giving up.\n");
			goto error;
		}
	}
	
	arcan_video_default_scalemode(scalemode);

	if (winx != -1 || winy != -1){
		char windbuf[64] = {0};
		snprintf(windbuf, 63, "SDL_VIDEO_WINDOW_POS=%i,%i", winx >= 0 ?
			winx : 0, winy >= 0 ? winy : 0);
		putenv(strdup(windbuf));
	}

	if (windowed)
		fullscreen = false;

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

#ifdef ARCAN_HMD
	arcan_hmd_setup();
#endif

/*
 * MINGW implements putenv, so use this to set
 * the system subpath path (BIOS, ..) 
 */
	if (getenv("ARCAN_SYSTEMPATH") == NULL){
		size_t len = strlen(arcan_resourcepath) + sizeof("/games/system") + 
			sizeof("ARCAN_SYSTEMPATH=") + 1;

		char* const syspath = malloc(len);
		sprintf(syspath, "ARCAN_SYSTEMPATH=%s/games/system", arcan_resourcepath);
		arcan_warning("Notice: Using default systempath (%s)\n", syspath);
		putenv(syspath);
	} else
		arcan_warning("Notice: Using systempath from environment (%s)\n", 
			getenv("ARCAN_SYSTEMPATH"));

	if (getenv("ARCAN_FRAMESERVER_LOGDIR") == NULL){
		size_t len = strlen(arcan_resourcepath) + sizeof("/logs") + 
			sizeof("ARCAN_FRAMESERVER_LOGDIR=/logs");

		char* const logpath = malloc(len);
		sprintf(logpath, "ARCAN_FRAMESERVER_LOGDIR=%s/logs", arcan_resourcepath);
		putenv(logpath);
	}

#ifndef _WIN32
	struct rlimit coresize = {0};

/* debuglevel 0, no coredumps etc.
 * debuglevel 1, maximum 1M coredump, should prioritize stack etc.
 * would want to be able to hint to mmap which pages that should be avoided
 * so that we could exclude texture data that both comprise most memory used
 * and can be recovered through other means. One option for this would be
 * to push image loading/management to a separate process, 
 * debuglevel > 1, dump everything */
	if (debuglevel == 0);
	else if (debuglevel == 1) coresize.rlim_max = 10 * 1024 * 1024;
	else coresize.rlim_max = RLIM_INFINITY;

	coresize.rlim_cur = coresize.rlim_max;
	setrlimit(RLIMIT_CORE, &coresize);
	system_page_size = sysconf(_SC_PAGE_SIZE);
#endif


/* setup VM, map arguments and possible overrides */ 
	struct arcan_luactx* luactx = arcan_lua_alloc();
	arcan_lua_mapfunctions(luactx, debuglevel);

	char* themescr = (char*) malloc(strlen(arcan_themename) + 5);
	sprintf(themescr, "%s.lua", arcan_themename);
	char* fn = arcan_find_resource(themescr, ARCAN_RESOURCE_THEME);

	char* msg = arcan_luaL_dofile(luactx, fn);
	if (msg != NULL){
		arcan_fatal("Fatal: main(), Error loading theme script"
			"(%s) : (%s)\n", themescr, msg);
		free(msg);
		goto error;
	}

	free(fn);
	free(themescr);

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

	arcan_lua_callvoidfun(luactx, "", true);
	arcan_lua_callvoidfun(luactx, "show", false);

	bool done = false, framepulse = true, feedtrig = true;
	float lastfrag = 0.0f;
	long long int lastflip = arcan_timemillis();
	int monitor_counter = monitor;
	
	arcan_event ev;
	arcan_evctx* evctx = arcan_event_defaultctx();

	while (!done) {
/* pollfeed can actually populate event-loops, assuming we don't exceed a 
 * compile- time threshold */
#ifdef ARCAN_HMD
		arcan_hmd_update();
#endif
		if (feedtrig){
			feedtrig = false;
			arcan_video_pollfeed();
		}

/* NOTE: might be better if this terminates if we're closing in on a 
 * deadline as to not be saturated with an onslaught of I/O events. */
		while (1 == arcan_event_poll(evctx, &ev)){ 

/*
 * these events can typically be determined in video_tick(),
 * however there are so many hierarchical dependencies 
 * (linked objs, instances, ...)
 * that a full delete is not really safe there (e.g. event -> callback -> 
 */
			switch (ev.category){
			case EVENT_VIDEO:
				if (ev.kind == EVENT_VIDEO_EXPIRE)
					arcan_video_deleteobject(ev.data.video.source);
			break;

			case EVENT_SYSTEM:
/* note the LUA shutdown() call actually emits this event */
				if (ev.kind == EVENT_SYSTEM_EXIT)
					done = true;
				else if (ev.kind == EVENT_SYSTEM_SWITCHTHEME){
					arcan_luaL_shutdown(luactx);
					arcan_video_shutdown();
					arcan_audio_shutdown();
					arcan_event_deinit(arcan_event_defaultctx());
					arcan_db_close(dbhandle);

					arcan_themename = strdup(ev.data.system.data.message);
					goto themeswitch;
				}
				else 
					continue;
			break;
			}

			arcan_lua_pushevent(luactx, &ev);
		}

		unsigned nticks;
		float frag = arcan_event_process(arcan_event_defaultctx(), &nticks);

		if (debuglevel == 4)
			arcan_warning("main() event_process (%d, %f)\n", nticks, frag);

/* priority is always in maintaining logical clock and event processing */
		if (nticks > 0){
			unsigned njobs;

			arcan_video_tick(nticks, &njobs);
			arcan_bench_register_tick(nticks);

			arcan_audio_tick(nticks);
			lastfrag = 0.0;
				
			if (monitor && !in_monitor){
				if (--monitor_counter == 0){
					static int mc;
					char buf[8];
					snprintf(buf, 8, "%d", mc++);
					monitor_counter = monitor;
					arcan_lua_statesnap(monitor_outf, buf, true);
				}
			}

/* debugging functionality to generate a dump and abort after n ticks */
			if (timedump){
				timedump -= nticks; 

				if (timedump <= 0){
					arcan_state_dump("timedump", "user requested a dump", __func__);
					break;
				}
			}	
		}

/* this is internally buffering and non-blocking, hence the fd use compared
 * to arcan_lua_statesnap above */
#ifndef _WIN32
		if (in_monitor)
			arcan_lua_stategrab(luactx, "sample", monitor_infd);
#endif
	
/*
 * difficult decision, should we flip or not?
 * a full- redraw can be costly, so should only really be done if 
 * enough things have changed or if we're closing in on the next 
 * deadline for the unknown video clock, this also depends on if 
 * the user favors energy saving (waitsleep) or responsiveness. 
 */
		const int min_respthresh = 9;

/* only render if there's enough relevant changes */
		if (!waitsleep || nticks > 0 || frag - lastfrag > INTERP_MINSTEP){

/* separate between cheap (possibly vsync off or triple buffering) 
 * flip cost and expensive (vsync on) */
			if (arcan_video_display.vsync_timing < 8.0){
				unsigned cost = arcan_video_refresh(frag, true);
				feedtrig = true;

				arcan_bench_register_cost(cost);
				arcan_bench_register_frame();
					if (framepulse)
						framepulse = arcan_lua_callvoidfun(luactx, "frame_pulse", false);

				int delta = arcan_timemillis() - lastflip;
				lastflip += delta;
	
				if (waitsleep && delta < min_respthresh)
					arcan_timesleep(min_respthresh - delta);
			}
			else {
				int delta = arcan_timemillis() - lastflip;
				if (delta >= (float)arcan_video_display.vsync_timing * vfalign){
					unsigned cost = arcan_video_refresh(frag, true);
					feedtrig = true;

					arcan_bench_register_cost(cost);
					arcan_bench_register_frame();
					if (framepulse)
						framepulse = arcan_lua_callvoidfun(luactx, "frame_pulse", false);

					lastflip += delta;
				} 
			}
		}

		arcan_audio_refresh();
	}

	arcan_lua_callvoidfun(luactx, "shutdown", false);

#ifdef ARCAN_LED
	arcan_led_shutdown();
#endif

#ifdef ARCAN_HMD
	arcan_hmd_shutdown();
#endif

	arcan_video_shutdown();

error:
	exit(1);

	return 0;
}
Exemplo n.º 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;
}