Personal tools


Interface.h

From OrganicDesign

(Redirected from Interface.c)
Jump to: navigation, search
// [[[[http://www.organicdesign.co.nz/peerd|peerd]]]] - nodal p2p wiki daemon
// This article and all its includes are licenced under LGPL
// GPL: [[[[http://www.gnu.org/copyleft/lesser.html]]]]
// SRC: [[[[http://www.organicdesign.co.nz/interface.c]]]]
// included in [[[[http://www.organicdesign.co.nz/category:peerd/files/C|peerd]]]][[[[http://www.organicdesign.co.nz/peerd.c|/peerd.c]]]]
// See also: [[[[http://www.organicdesign.co.nz/talk:interface.c|talk page]]]], [[[[http://www.libsdl.org/index.php|libSDL.org]]]], [[[[http://www.libsdl.org/cgi/docwiki.cgi|SDL Wiki]]]]
// Currently working on [[[[recursive rectangles]]]] milestone
 
// Prototypes
void ifInit();
void ifExit();
 
void frame();
void click();
void render();
 
void spriteInit();
void spriteExit();
 
void audioInit();
void audioMain(void *udata,Uint8 *stream,int len);
 
void videoInit();
void videoResize();
SDL_Surface *videoLoadImage(char *file);
 
// spriteInfo structure to represent a nodal [[[[layer]]]]
typedef struct {
	int index,children;
	int x,y,w,h,t;
	double scale,rotation;
	Uint32 fill;
	SDL_Surface *image;
	TTF_Font *font;
	char *text;
	} spriteInfo;
 
// SDL globals
node n;
SDL_Surface *surface, *test_image;
SDL_Event event;
int bpp = 0, surfaceW = 640, surfaceH = 480, f = 0;
int flags = SDL_HWSURFACE|SDL_RESIZABLE|SDL_DOUBLEBUF;
int zorder;
TTF_Font *test_font;
 
// Audio test - see [[[[http://www.libsdl.org/cgi/docwiki.cgi/Audio_20Examples|SDL Audio Examples]]]]
SDL_AudioSpec *wav;
Uint32 wavLen, wavCtr;
Uint8 *wavBuf, *wavPtr;
 
 
// ----------------------------------------------------------------------------------------- //
// interface.c
 
void ifInit() {
 
	// Hook render-workflow into [[[[desktop]]]]
	*nodeState(nodeRENDER,nodeCODE) = &render;
	nodeLoopInsert(nodeDESKTOP,nodeRENDER);
 
	// Set up a spriteInfo struct for [[[[desktop]]]] children to base their metrics on
	spriteInfo *sprite = malloc(sizeof(spriteInfo));
	sprite->index = 0;
	sprite->children = 0;
	sprite->rotation = 0;
	sprite->scale = 1;
	*nodeState(nodeDESKTOP,nodeSPRITE) = sprite;
 
	// Initialise SDL audio and OpenGL video
	videoInit();
	audioInit();
 
	// Initialise fonts and load test font
	if (TTF_Init() == -1) { logAdd("TTF_Init: %s\n", TTF_GetError()); exit(2); }
	else {
		// load font.ttf at size 16 into font
		test_font = TTF_OpenFont("./times.ttf",40);
		if (!test_font) logAdd("TTF_OpenFont: %s\n", TTF_GetError());
		else logAdd("Fonts successfully initialised and test font loaded");
		}
	}
 
void ifExit() {
	SDL_FreeWAV(wavBuf);
	SDL_CloseAudio();
	SDL_Quit();
	}
 
 
// ----------------------------------------------------------------------------------------- //
// Nodal event functions (events,click)
 
// per-frame function
void frame() {
 
	// Hook rendering workflow in to [[[[desktop]]]]
	nodeLoopInsert(nodeDESKTOP,nodeRENDER);
	*nodeState(nodeRENDER,nodeCODE) = &render;
 
	// Check if any pending events
	if (SDL_PollEvent(&event)) {
		if (event.type == SDL_QUIT) nodeExit(); // Program exit
		if (event.type == SDL_VIDEORESIZE) {    // Resize window or screen
			surfaceW = event.resize.w;
			surfaceH = event.resize.h;
			surface = SDL_SetVideoMode(surfaceW,surfaceH,bpp,flags);
			videoResize();
			}
		if (event.type == SDL_KEYDOWN) {
			SDL_SaveBMP(surface, "./screenie.bmp");
			logAdd("./screenie.bmp saved");
			}
		if (event.type == SDL_MOUSEBUTTONDOWN) {
 
			// Store the mouse event metrics on a spriteInfo struct
			spriteInfo *mouse = malloc(sizeof(spriteInfo));
			mouse->x = event.button.x-surfaceW/2;
			mouse->y = event.button.y-surfaceH/2;
			mouse->w = mouse->h = 0;
 
			// Create new click-node and hook in to [[[[desktop]]]]'s [[[[loop]]]]
			n = nodeLoopInsert(nodeDESKTOP,0);
			*nodeState(n,nodeSPRITE) = mouse;
			*nodeState(n,nodeCODE) = &click;
			logAdd("node %d created for mouse click event",n);
			}
		}
 
	// Update video display
	SDL_Flip(surface);
	SDL_SetClipRect(surface,NULL);
	SDL_FillRect(surface,NULL,0);
	zorder = 0;
	f++;
	}
 
 
// Collision workflow
// - &click is in the [[[[loop]]]] of the sprite-node we're checking the bounds for
void click() {
 
	// Cmp ptr with bounds of the sprite we're currently hooked into the [[[[loop]]]] of
	int outside = 1;
	spriteInfo *sprite = *nodeState(parent,nodeSPRITE);
	if (sprite) {
 
		// Get position of mouse pointer
		spriteInfo *pointer = *nodeState(this,nodeSPRITE);
		int px = pointer->x;
		int py = pointer->y;
 
		// Cmp with parent sprite bounds
		int w = sprite->w;
		int h = sprite->h;
		int x = sprite->x;
		int y = sprite->y;
		outside = (px<x-w/2 || x+w/2<px || py<y-h/2 || y+h/2<py);
		logAdd("(%d,%d) : (%d,%d,%d,%d)",px,py,x-w/2,y-h/2,w,h);
		}
 
	// Move this workflow depending on inside/outside result
	if (outside) {
		// Pointer is outside parent bounds, pass this workflow to parent's next sibling
		nodeLoopRemove(parent,this);
		if (nodeGetValue(grandpa,nodeLAST) == parent) { // no next sibling, so grandpa is recipient
			nodeLoopInsert(grandpa,this);
			*nodeState(this,nodeCODE) = &spriteInit;
			}
		else nodeLoopInsert(nodeGetValue(parent,nodeNEXT),this);
		}
	else {
		// Pointer inside, pass to first child if exists, if no children [[[[this]]]] is recipient
		if (n = nodeGetValue(parent,nodeFIRST)) {
			nodeLoopRemove(parent,this);
			nodeLoopInsert(n,this);
			node x=nodeGetValue(n,0);
logAdd("    hooked %d into %d (%d<-%d->%d)",this,n,nodeGetValue(x,nodePREV),x,nodeGetValue(x,nodeNEXT));
			}
		else *nodeState(this,nodeCODE) = &spriteInit;
		}
	}
 
 
// Draw current sprite
// - this is the rendering workflow which is passed around to complete a frame render
// - each should update the redraw tree which is then rendered in desktop() because currently every rect is a separate [[[[layer]]]]
void render() {
 
	spriteInfo *sprite = *nodeState(parent,nodeSPRITE);
	if ((parent != nodeDESKTOP) && nodeGetValue(parent,nodeSPRITE) && sprite->scale) {
 
		// Calculate new sprite metrics from index and [[[[parent]]]] metrics
		spriteInfo *psi = *nodeState(grandpa,nodeSPRITE);
		int wpl = 2;
		int i = sprite->index;
		int w = sprite->w = psi->w/wpl;
		int h = sprite->h = psi->h/wpl;
		int x = sprite->x = i%wpl*w+w/2+psi->x-psi->w/2;
		int y = sprite->y = i/wpl*h+h/2+psi->y-psi->h/2;
		double t = f - sprite->t;
		double s = ((t=t*t)<20000) ? sprite->scale - sin(t/500)*600/t : sprite->scale;
		w = s*w/2;
		h = s*h/2;
 
		// Fill sprite background (colour based on index currently not sprite->fill)
		int j = zorder++%6+1;
		SDL_Rect bounds = {x-w+surfaceW/2,y-h+surfaceH/2,w*2,h*2};
		SDL_SetClipRect(surface,&bounds);
		SDL_FillRect(surface,NULL,(j&1?0xff:0x80)|(j&2?0xff00:0x8000)|(j&4?0xff0000:0x800000));
 
		// Draw image if any
		if (sprite->image) {
			int r = 2*w/3;
			SDL_Rect bounds = {x-r+surfaceW/2,y-r+surfaceH/2,r*2,r*2};
			SDL_BlitSurface(sprite->image,NULL,surface,&bounds);
			}
 
		// Render text if any
		if (sprite->text && sprite->font) {
			SDL_Color color = {j&1?0x80:0xff,j&2?0x80:0xff,j&4?0x80:0xff};
			SDL_Surface *txt;
			txt = TTF_RenderText_Blended(sprite->font,sprite->text,color);
			SDL_Rect bounds = {x-txt->w/2+surfaceW/2,y-txt->h/2+surfaceH/2,txt->w,txt->h};
			SDL_BlitSurface(txt,NULL,surface,&bounds);
			SDL_FreeSurface(txt);
			}
		}
 
	// Determine next [[[[parent]]]] for this [[[[nodal workflow|workflow]]]] (or 0 if done)
	node p = parent, q = grandpa, n = nodeGetValue(parent,nodeFIRST);
	while ((n == 0) && (q!= nodeSESSION))
		if (nodeGetValue(q,nodeLAST) == p) q = nodeGetValue(p = q,nodePARENT);
		else n = nodeGetValue(p,nodeNEXT);
 
	// Move render-workflow or call frame() if finished
	nodeLoopRemove(parent,this);
	if (n) nodeLoopInsert(n,this); else frame();
	this = 0;
	}
 
 
// ----------------------------------------------------------------------------------------- //
// Nodal sprite functions (INIT,MAIN,EXIT)
 
// Create new sprite-node and spriteInfo structure from passed mouse-click-coordindates
// - called nodally so that [[[[parent]]]] and [[[[this]]]] are properly set
// - hooked in by the &click workflow once click-recipient resolved
void spriteInit() {
 
logAdd("spriteInit()");
	// insert new sprite-node into [[[[parent]]]]'s [[[[loop]]]]
	n = nodeLoopInsert(parent,0);
 
	// Get parent spriteInfo
	spriteInfo *psi = *nodeState(parent,nodeSPRITE);
 
	// Create new spriteInfo structure
	spriteInfo *sprite = malloc(sizeof(spriteInfo));
	sprite->index = psi->children++; // increment parent's child count
	sprite->children = 0;
	sprite->t = f-50;
	sprite->rotation = 0;
	sprite->scale = 1;
	sprite->image = 0; // test_image removed since plain-vanilla SDL has no scaling or rotation
	sprite->font = test_font;
	sprite->text = "Hello World!";
 
	// Create the new node and update parent's first and last child info
	*nodeState(n,nodeSPRITE) = sprite;
	if (nodeGetValue(parent,nodeFIRST)==0) nodeSetValue(parent,nodeFIRST,n);
	nodeSetValue(parent,nodeLAST,n);
 
	// Play the test wav sample
	wavCtr = wavLen;
	wavPtr = wavBuf;
	SDL_PauseAudio(0);
 
	// Remove the click-event node now that its been serviced (can use spriteExit since we used a spriteInfo struct)
	spriteExit();
	logAdd("New sprite (%d/%d/%d) created, click-event (%d) removed.",grandpa,parent,n,this);
	this = 0;
	}
 
 
// Remove the sprite represented by [[[[this]]]] and free its spriteInfo structure
void spriteExit() {
	spriteInfo *sprite = *nodeState(nodeLoopRemove(parent,this),nodeSPRITE);
	if (sprite) free(sprite);
	}
 
 
// ----------------------------------------------------------------------------------------- //
// Audio (Init, Main)
 
// Load test audio wav - see [[[[http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fOpenAudio|SDL_OpenAudio]]]], [[[[http://www.libsdl.org/cgi/docwiki.cgi/SDL_5fLoadWAV|SDL_LoadWav]]]]
void audioInit() {
	wav = malloc(sizeof(SDL_AudioSpec));
	wav->freq = 11025;
	wav->format = AUDIO_U8;
	wav->channels = 1;
	wav->callback = &audioMain;
	wav->samples = 512;
	wav->userdata = NULL;
	SDL_AudioSpec *obtained = malloc(sizeof(SDL_AudioSpec));
	SDL_OpenAudio(wav, obtained);
	if (obtained) wav = obtained;
	if (SDL_LoadWAV("./test.wav",wav,&wavBuf,&wavLen)==NULL) {
		int i=wavLen=2000;
		wavBuf = malloc(wavLen);
		while (i--) wavBuf[i] = i%0xff;
		}
	logAdd("wav->samples = %d",wav->samples);
	}
 
 
// Callback for audio playing
void audioMain(void *udata,Uint8 *stream,int len) {
	if (wavCtr == 0) return; // exit if no data left to play
	if (len > wavCtr) len = wavCtr;
	wavCtr -= len;
	while(len--) *stream++ = *wavPtr++;
	}
 
 
// ----------------------------------------------------------------------------------------- //
// Video (Init, LoadImage, Resize)
 
// Set up main video surface & window
void videoInit() {
	if ((SDL_Init(SDL_INIT_VIDEO)<0)) {
		putenv("SDL_VIDEODRIVER=dummy"); // fallback to dummy driver if video won't init
		if ((SDL_Init(SDL_INIT_VIDEO)<0)) logAdd("SDL initialisation failed! (%s)",SDL_GetError());
		}
 
	const SDL_VideoInfo* video;
	if ((video=SDL_GetVideoInfo())<0) logAdd("getVideoInfo() failed returning \"%s\"",SDL_GetError());
	else bpp = video->vfmt->BitsPerPixel;
 
	surface = SDL_SetVideoMode(surfaceW,surfaceH,bpp,flags);
	if (surface == NULL) logAdd("setVideoMode() failed returning \"%s\"",SDL_GetError());
	if (errno == 0) logAdd("SDL successfully initialized.");
	SDL_WM_SetCaption(peer,peer);
	videoResize();
	test_image = videoLoadImage("./wheel.png");
	}
 
 
// Loads image from passed filename and returns it as a surface
SDL_Surface *videoLoadImage(char *file) {
	SDL_Surface *img = IMG_Load(file);
	int Bpp = img->format->BytesPerPixel;
	if (img) logAdd("Image \"%s\" loaded (%dx%d@%dbpp)",file,img->w,img->h,Bpp*8);
	else logAdd("videoLoadImage(): IMG_Load failed! (%s)",IMG_GetError());
	return img;
	}
 
 
// Adjust OpenGL setup on resize
void videoResize() {
	spriteInfo *sprite = *nodeState(nodeDESKTOP,nodeSPRITE);
	sprite->x = 0;
	sprite->y = 0;
	sprite->w = surfaceW;
	sprite->h = surfaceH;
	}