///////////////////////////////////////////////////////////// // Flash Plugin and Player // Copyright (C) 1998,1999 Olivier Debon // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // /////////////////////////////////////////////////////////////// // Author : Olivier Debon // #include "swf.h" #define NOTHING 0x0 #define WAKEUP 0x1 #define GOTO 0x2 #define REFRESH 0x4 #ifdef RCSID static char *rcsid = "$Id$"; #endif #define PRINT 0 int debug = 0; Program::Program(FlashMovie *movie, long n) { long f; this->movie = movie; totalFrames = 0; dl = new DisplayList(movie); if (dl == NULL) return; frames = new Frame[n]; if (frames == NULL) { delete dl; return; } nbFrames = 0; totalFrames = n; currentFrame = 0; loadingFrame = 0; movieWait = 1; nextFrame = currentFrame; for(f = 0; f < n; f++) { frames[f].controls = 0; frames[f].label = NULL; } movieStatus = MoviePlay; settings = 0; } Program::~Program() { int i; Control *ctrl, *ctrl1; delete dl; if (frames != NULL) { for(i=0;inext; ctrl->next = NULL; delete ctrl; ctrl = ctrl1; } } delete[] frames; } } void Program::validateLoadingFrame() { nbFrames = loadingFrame; loadingFrame++; movieWait = 0; } Frame * Program::getFrames() { return frames; } long Program::getNbFrames() { return nbFrames; } DisplayList * Program::getDisplayList() { return dl; } long Program::getCurrentFrame() { return currentFrame; } void Program::setCurrentFrame(long n) { currentFrame = n; nextFrame = n; //refresh = 1; } void Program::gotoFrame(GraphicDevice *gd, long frame) { long f; //printf("GotoFrame %d (Current = %d)\n", frame, currentFrame); dl->clearList(); for(f=0; f <= frame; f++) { runFrame(gd, 0, f, 0); } } long Program::runFrame(GraphicDevice *gd, SoundMixer *sm, long f, long action) { Control *ctrl; Character *character; Matrix *matrix; Cxform *cxform; long status = NOTHING; long update = 0; char *name; #if PRINT&1 if (action) printf("Prog %x (dl=%x): Frame N° %d/%d\n", this, this->dl, f, nbFrames-1); #endif movie->buttons_updated = 0; for(ctrl = frames[f].controls; ctrl; ctrl = ctrl->next) { switch (ctrl->type) { case ctrlPlaceObject: case ctrlPlaceObject2: character = 0; matrix = 0; cxform = 0; name = ""; if (ctrl->flags & placeHasCharacter) { character = ctrl->character; } if (ctrl->flags & placeHasMatrix) { matrix = &ctrl->matrix; } if (ctrl->flags & placeHasColorXform) { cxform = &ctrl->cxform; } if (ctrl->flags & placeHasName) { name = ctrl->name; } if (!ctrl->clipDepth) { // Ignore dl->placeObject(gd,character, ctrl->depth, matrix, cxform, name); update = 1; } break; case ctrlRemoveObject: character = ctrl->character; if (!character) break; // Should not happen dl->removeObject(gd, character, ctrl->depth); if (action) { character->reset(); update = 1; } break; case ctrlRemoveObject2: character = dl->removeObject(gd,NULL, ctrl->depth); if (character && action) { character->reset(); update = 1; } break; // Actions case ctrlDoAction: if (action) { status = doAction(gd, ctrl->actionRecords, sm); } break; case ctrlStartSound: if (action && sm) { sm->startSound( (Sound *)ctrl->character ); } break; case ctrlStopSound: if (action && sm) { sm->stopSounds(); } break; case ctrlBackgroundColor: if (action) { if (gd->setBackgroundColor(ctrl->color)) { dl->bbox.xmin = -32768; dl->bbox.ymin = -32768; dl->bbox.xmax = 32768; dl->bbox.ymax = 32768; } } break; } } if (movie->buttons_updated) { dl->updateButtons(movie); } if (status & GOTO) { if (nextFrame < nbFrames) { gotoFrame(gd,nextFrame); if (nextFrame != f) if (movieStatus == MoviePaused) runFrame(gd,sm,nextFrame); update = 1; } } #if PRINT&1 if (action) printf("Frame N° %d ready\n", f); #endif return update; } long Program::nestedMovie(GraphicDevice *gd, SoundMixer *sm, Matrix *mat, Cxform *cxform) { if (movieStatus == MoviePlay) { // Movie Beeing Played advanceFrame(); if (currentFrame == 0) { dl->clearList(); } runFrame(gd, sm, currentFrame); if (nbFrames == 1) { pauseMovie(); } } return (movieStatus == MoviePlay); } long Program::processMovie(GraphicDevice *gd, SoundMixer *sm) { int wakeUp = 0; #if PRINT&1 printf("Prog %x (dl=%x): Current = %d Next = %d Wait = %d Status = %d\n", this, this->dl, currentFrame, nextFrame, movieWait, movieStatus); #endif if (movieStatus == MoviePlay && movieWait == 0) { // Movie Beeing Played advanceFrame(); if (currentFrame == 0) { dl->clearList(); } wakeUp |= runFrame(gd, sm, currentFrame); wakeUp |= dl->updateSprites(); if (nextFrame == nbFrames) { if (nbFrames != totalFrames) { movieWait = 1; } else if ((settings & PLAYER_LOOP) == 0) { pauseMovie(); } } } else { wakeUp |= dl->updateSprites(); } if (wakeUp) { render = 1; } return (wakeUp || movieStatus == MoviePlay); } /* timer (ms) -1 = delete timer */ void setFlashTimer(struct timeval *tv, int time_ms) { if (time_ms == -1) { tv->tv_sec = -1; } else { gettimeofday(tv,0); tv->tv_usec += time_ms*1000; while (tv->tv_usec > 1000000) { tv->tv_usec -= 1000000; tv->tv_sec++; } } } int checkFlashTimer(struct timeval *tv) { struct timeval now; if (tv->tv_sec == -1) return 0; gettimeofday(&now,0); return (now.tv_sec > tv->tv_sec || (now.tv_sec == tv->tv_sec && now.tv_usec >= tv->tv_usec)); } /* bbox */ typedef struct { long x1,y1,x2,y2; } ButtonBoundingBox; static void button_bbox_func(void *id, long y, long start, long end) { ButtonBoundingBox *h = (ButtonBoundingBox *) id; if (y < h->y1) h->y1 = y; if (y > h->y2) h->y2 = y; if (start < h->x1) h->x1 = start; if (end > h->x2) h->x2 = end; } void computeBBox(FlashMovie *movie, Rect *rect, DisplayListEntry *e) { ButtonBoundingBox bb; bb.x1 = LONG_MAX; bb.y1 = LONG_MAX; bb.x2 = LONG_MIN; bb.y2 = LONG_MIN; e->character->getRegion(movie->gd,&e->renderMatrix,&bb,button_bbox_func); rect->xmin = bb.x1 / FRAC; rect->xmax = bb.x2 / FRAC; rect->ymin = bb.y1; rect->ymax = bb.y2; } void transform_coords(long *x_ptr,long *y_ptr, long cx, long cy, long dx, long dy) { long x,y,x1,y1; x = *x_ptr; y = *y_ptr; x -= cx; y -= cy; if (dx < 0) { /* left */ x1 = - x; y1 = y; } else if (dy < 0) { /* up */ y1 = x; x1 = -y; } else if (dy > 0) { /* down */ y1 = x; x1 = y; } else { /* right */ x1 = x; y1 = y; } *x_ptr = x1; *y_ptr = y1; } typedef struct { FlashMovie *movie; DisplayListEntry *emin,*cur_focus; long dmin; long w,cx,cy,dx,dy; } ButtonFocus; static int button_focus(void *opaque, Program *prg, DisplayListEntry *e) { ButtonFocus *h=(ButtonFocus *)opaque; Rect rect; long d,x,y; if (e != h->cur_focus) { computeBBox(h->movie,&rect,e); x = (rect.xmin + rect.xmax) / 2; y = (rect.ymin + rect.ymax) / 2; /* transform the coords so that the angular sector is directed to the right */ transform_coords(&x,&y,h->cx,h->cy,h->dx,h->dy); /* inside it ? */ if ( x >= 0 && (y - x - h->w) <= 0 && (y + x + h->w) >= 0) { d = x*x + y*y; if (d < h->dmin) { h->dmin = d; h->emin = e; } } } return 0; } DisplayListEntry *moveFocus(FlashMovie *movie, long dx, long dy, DisplayListEntry *cur_focus) { Rect cur_rect; ButtonFocus h; h.movie = movie; h.dx = dx; h.dy = dy; computeBBox(movie,&cur_rect,cur_focus); /* center */ h.cx = (cur_rect.xmin + cur_rect.xmax) / 2; h.cy = (cur_rect.ymin + cur_rect.ymax) / 2; /* width/2 of the 45 degrees angular sector */ if (dy != 0) { /* for vertical displacement, we have a larger width */ h.w = (cur_rect.xmax - cur_rect.xmin) / 2; } else { /* zero width for horizontal displacement */ h.w = 0; } /* now we select the nearest button in the angular sector */ h.dmin = LONG_MAX; h.emin = NULL; h.cur_focus = cur_focus; exploreButtons(movie, &h, button_focus); return h.emin; } static int button_newfocus(void *opaque, Program *prg, DisplayListEntry *e) { * (DisplayListEntry **)opaque = e; return 2; } static int button_nextfocus(void *opaque, Program *prg, DisplayListEntry *e) { static int found = 0; DisplayListEntry **focus; focus = (DisplayListEntry **)opaque; if (found) { *focus = e; found = 0; return 2; } if (e == *focus) { found = 1; } return 0; } /* XXX: should not be here (one level upper) */ long Program::handleEvent(GraphicDevice *gd, SoundMixer *sm, FlashEvent *fe) { ActionRecord *action; Program *prog; long status = 0; DisplayListEntry *cur_focus, *new_focus; long dx,dy; int refresh; refresh = 0; switch(fe->type) { case FeKeyRelease: if (movie->mouse_active == 0) { if (movie->cur_focus) { movie->cur_focus->owner->updateBoundingBox(movie->cur_focus); movie->cur_focus->renderState = stateOver; movie->cur_focus->owner->updateBoundingBox(movie->cur_focus); } } break; case FeKeyPress: movie->mouse_active = 0; /* find the button which has the focus */ cur_focus = movie->cur_focus; if (fe->key == FeKeyEnter) { /* selection */ if (cur_focus) { /* select the button */ cur_focus->owner->updateBoundingBox(cur_focus); cur_focus->renderState = stateDown; ((Button *)cur_focus->character)->updateButtonState(cur_focus); cur_focus->owner->updateBoundingBox(cur_focus); movie->scheduledEvent.type = FeKeyRelease; movie->scheduledEvent.key = FeKeyEnter; setFlashTimer(&movie->scheduledTime, 250); /* 250 ms down */ } } else { /* displacement */ if (cur_focus == NULL) { /* no current focus : set one */ exploreButtons(movie, &cur_focus, button_newfocus); if (cur_focus) { cur_focus->renderState = stateOver; ((Button *)cur_focus->character)->updateButtonState(cur_focus); cur_focus->owner->updateBoundingBox(cur_focus); } movie->cur_focus = cur_focus; } else { /* move the focus (test) */ switch(fe->key) { case FeKeyNext: /* Next available */ cur_focus->owner->updateBoundingBox(cur_focus); cur_focus->renderState = stateUp; ((Button *)cur_focus->character)->updateButtonState(cur_focus); cur_focus->owner->updateBoundingBox(cur_focus); exploreButtons(movie, &cur_focus, button_nextfocus); if (cur_focus) { cur_focus->renderState = stateOver; ((Button *)cur_focus->character)->updateButtonState(cur_focus); cur_focus->owner->updateBoundingBox(cur_focus); } movie->cur_focus = cur_focus; dx = 0; dy = 0; break; case FeKeyUp: dx = 0; dy = -1; break; case FeKeyDown: dx = 0; dy = 1; break; case FeKeyLeft: dx = -1; dy = 0; break; case FeKeyRight: dx = 1; dy = 0; break; default: /* should not happen */ dx = 0; dy = 0; break; } if (dx != 0 || dy != 0) { new_focus = moveFocus(movie, dx, dy, cur_focus); if (new_focus) { cur_focus->owner->updateBoundingBox(cur_focus); cur_focus->renderState = stateUp; ((Button *)cur_focus->character)->updateButtonState(cur_focus); cur_focus->owner->updateBoundingBox(cur_focus); if (computeActions(movie, &prog, &action)) { status |= prog->doAction(gd, action, sm); } new_focus->renderState = stateOver; ((Button *)new_focus->character)->updateButtonState(new_focus); movie->cur_focus = new_focus; new_focus->owner->updateBoundingBox(new_focus); } else { return 0; } } } if (movie->cur_focus == NULL) return 0; } break; case FeMouseMove: movie->mouse_active = 1; movie->mouse_x = fe->x * FRAC; movie->mouse_y = fe->y * FRAC; dl->updateButtons(movie); break; case FeButtonPress: movie->mouse_active = 1; movie->button_pressed = 1; dl->updateButtons(movie); break; case FeButtonRelease: movie->mouse_active = 1; movie->button_pressed = 0; dl->updateButtons(movie); break; default: return 0; } if (computeActions(movie, &prog, &action)) { status |= prog->doAction(gd, action, sm); } if (status & REFRESH) { status |= WAKEUP; refresh = 1; } if (status & GOTO) { if (nextFrame < nbFrames) { gotoFrame(gd, nextFrame); if (movieStatus == MoviePaused) runFrame(gd,sm,nextFrame); refresh = 1; } } if (refresh) { dl->updateSprites(); render = 1; } return (refresh || movieStatus == MoviePlay); } long Program::doAction(GraphicDevice *gd, ActionRecord *action, SoundMixer *sm) { long status = NOTHING; long f; char *target = ""; long skip = 0; while(action) { if (skip) skip--; else switch (action->action) { case ActionPlaySound: #if PRINT&2 printf("Prog %x : PlaySound\n", this); #endif if (sm) { sm->startSound(action->sound); } status |= WAKEUP; break; case ActionRefresh: #if PRINT&2 printf("Prog %x : Refresh\n", this); #endif status |= REFRESH; break; case ActionGotoFrame: #if PRINT&2 printf("Prog %x : GotoFrame %d\n", this, action->frameIndex); #endif if (target[0] == 0) { if (action->frameIndex < nbFrames) { currentFrame = action->frameIndex; pauseMovie(); status |= WAKEUP|GOTO; } } break; case ActionGetURL: #if PRINT&2 printf("Prog %x : GetURL %s target = %s\n", this, action->url, action->target); #endif { int len,level; len = strlen(action->target); if (len > 6 && memcmp(action->target,"_level", 6) == 0) { level = atoi(action->target + 6); loadNewSwf(movie, action->url, level); } else { if (movie->getUrl) { movie->getUrl(action->url, action->target, movie->getUrlClientData); } } } break; case ActionNextFrame: nextFrame = currentFrame+1; movieStatus = MoviePlay; status |= WAKEUP; break; case ActionPrevFrame: nextFrame = currentFrame-1; status |= WAKEUP|GOTO; break; case ActionPlay: #if PRINT&2 printf("Prog %x : Play\n", this); #endif if (target[0] == 0) { movieStatus = MoviePlay; if ((status & GOTO) == 0) { if (currentFrame == nextFrame) advanceFrame(); } status |= WAKEUP; } break; case ActionStop: #if PRINT&2 printf("Prog %x : Stop\n", this); #endif if (target[0] == 0) { movieStatus = MoviePaused; nextFrame = currentFrame; } break; case ActionToggleQuality: break; case ActionStopSounds: if (sm) { sm->stopSounds(); } break; case ActionWaitForFrame: if (action->frameIndex >= nbFrames) { skip = action->skipCount; } break; case ActionSetTarget: #if PRINT&2 printf("Prog %x : SetTarget '%s'\n", this, action->target); #endif target = action->target; break; case ActionGoToLabel: #if PRINT&2 printf("Prog %x : GotoFrame '%s'\n", this, action->frameLabel); #endif f = searchFrame(gd, action->frameLabel, target); if (f >= 0) { currentFrame = f; pauseMovie(); status |= WAKEUP|GOTO; } else { status |= REFRESH; } break; } action = action->next; } return status; } void Program::setCurrentFrameLabel(char *label) { frames[loadingFrame].label = label; } void Program::rewindMovie() { currentFrame = 0; nextFrame = 0; } void Program::pauseMovie() { movieStatus = MoviePaused; nextFrame = currentFrame; } void Program::continueMovie() { movieStatus = MoviePlay; } void Program::nextStepMovie() { if (movieStatus == MoviePaused) { advanceFrame(); } } void Program::advanceFrame() { currentFrame = nextFrame; nextFrame = currentFrame+1; if (currentFrame == nbFrames) { currentFrame = 0; nextFrame = 0; movieStatus = MoviePlay; } } void Program::addControlInCurrentFrame(Control *ctrl) { Control *c; ctrl->next = 0; if (frames[loadingFrame].controls == 0) { frames[loadingFrame].controls = ctrl; } else { for(c = frames[loadingFrame].controls; c->next; c = c->next); c->next = ctrl; } } void Program::modifySettings(long flags) { settings = flags; } long Program::searchFrame(GraphicDevice *gd, char *label, char *target) { long f; DisplayListEntry *e; Program *prg; // Current movie if (target[0] == 0) { for(f=0; f < nbFrames; f++) { if (frames[f].label && !strcmp(label,frames[f].label)) { return f; } } } // Kludge !!! for (e = dl->list; e; e = e->next) { if (e->character->isSprite()) { prg = ((Sprite *)e->character)->program; f = prg->searchFrame(gd,label,""); if (f >= 0 && f < prg->nbFrames) { prg->dl->updateBoundingBox(e); prg->gotoFrame(gd, f); prg->nextFrame = f; prg->dl->updateBoundingBox(e); return -1; } } } return -1; } void loadNewSwf(FlashMovie *movie, char *url, int level) { CInputScript *s,*prev,**l; if (movie->getSwf == NULL) return; for(s = movie->main, prev = 0; s != NULL; prev = s, s = s->next) { if (s->level == level) { // Mark movie to be deleted s->level = -1; break; } } //printf("Unload movie @ %d\n", level); if (*url == 0) return; // Just UnloadMovie s = new CInputScript(level); if (s == NULL) return; /* insert it in the right order */ l = &movie->main; while (*l != NULL && (*l)->level < level) l = &(*l)->next; s->next = *l; *l = s; // Notify the external loader of a new movie to load movie->getSwf(url, level, movie->getSwfClientData); }