Difference between revisions of "Interface.c"
From Organic Design wiki
(Article updated via HTTP request) |
m (Removed redirect to Interface.h) (Tag: Removed redirect) |
||
Line 1: | Line 1: | ||
− | + | {{legacy}} | |
+ | <source lang="c"> | ||
+ | // [[[[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; | ||
+ | } | ||
+ | </source> | ||
+ | [[Category:C]] |
Latest revision as of 13:10, 13 December 2019
// [[[[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;
}