Include program counter on action limit notification log
[gnash.git] / cygnal / cvm.cpp
blob5d32e463cfaed46e35dfc78d0d200b71de9c119b
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
3 // Foundation, Inc
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
24 #include "NullSoundHandler.h"
25 #ifdef USE_FFMPEG
26 # include "ffmpeg/MediaHandlerFfmpeg.h"
27 #elif defined(USE_GST)
28 # include "gst/MediaHandlerGst.h"
29 #endif
31 #include <iostream>
32 #include <cstdio>
33 #include <cstdlib>
34 #include <sys/time.h>
35 #include <ctime>
37 #ifdef ENABLE_NLS
38 # include <clocale>
39 #endif
41 #include "ClockTime.h"
42 #include "movie_definition.h"
43 #include "MovieClip.h"
44 #include "movie_root.h"
45 #include "log.h"
46 #include "rc.h"
47 #include "handler.h"
48 #include "URL.h"
49 #include "GnashException.h"
50 #include "debugger.h"
51 #include "VM.h"
52 #include "noseek_fd_adapter.h"
53 #include "ManualClock.h"
54 #include "StringPredicates.h"
55 #include "smart_ptr.h"
56 #include "IOChannel.h" // for proper dtor call
57 #include "GnashSleep.h" // for usleep comptibility.
58 #include "MovieFactory.h"
59 #include "RunResources.h"
61 extern "C"{
62 #ifdef HAVE_GETOPT_H
63 #include <getopt.h>
64 #endif
65 #ifndef __GNUC__
66 extern char *optarg;
67 extern int optopt;
68 extern int optind, getopt(int, char *const *, const char *);
69 #endif
72 // How many seconds to wait for a frame advancement
73 // before kicking the movie (forcing it to next frame)
74 static const double waitforadvance = 5;
76 // How many time do we allow for loop backs
77 // (goto frame < current frame)
78 static const size_t allowloopbacks = 10;
80 // How many times to call 'advance' ?
81 // If 0 number of advance is unlimited
82 // (see other constraints)
83 // TODO: add a command-line switch to control this
84 static size_t limit_advances = 0;
86 // How much time to sleep between advances ?
87 // If set to -1 it will be computed based on FPS.
88 static long int delay = 0;
90 const char *GPROC_VERSION = "1.0";
92 using namespace std;
93 using namespace gnash;
95 namespace {
96 gnash::LogFile& dbglogfile = gnash::LogFile::getDefaultInstance();
97 gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
98 #ifdef USE_DEBUGGER
99 gnash::Debugger& debugger = gnash::Debugger::getDefaultInstance();
100 #endif
103 namespace cygnal {
105 struct movie_data
107 gnash::movie_definition* m_movie;
108 std::string m_filename;
111 static boost::intrusive_ptr<gnash::movie_definition> play_movie(
112 const std::string& filename, const RunResources& runResources);
114 static bool s_do_output = false;
115 static bool s_stop_on_errors = true;
117 // How many time do we allow to hit the end ?
118 static size_t allowed_end_hits = 1;
120 double lastAdvanceTimer;
122 void
123 resetLastAdvanceTimer()
125 // clocktime::getTicks() returns milliseconds
126 lastAdvanceTimer = static_cast<double>(clocktime::getTicks()) / 1000.0;
129 double
130 secondsSinceLastAdvance()
132 // clocktime::getTicks() returns milliseconds
133 double now = static_cast<double>(clocktime::getTicks()) / 1000.0;
134 return ( now - lastAdvanceTimer);
137 // A flag which will be used to interrupt playback
138 // by effect of a "quit" fscommand
140 static int quitrequested = false;
142 class FsCommandExecutor: public movie_root::AbstractFsCallback {
143 public:
144 void notify(const std::string& command, const std::string& args)
146 log_debug(_("fs_callback(%p): %s %s"), command, args);
148 StringNoCaseEqual ncasecomp;
150 if ( ncasecomp(command, "quit") ) quitrequested = true;
154 class EventCallback: public movie_root::AbstractIfaceCallback {
155 public:
156 std::string call(const std::string& event, const std::string& arg)
158 log_debug(_("eventCallback: %s %s"), event, arg);
160 static bool mouseShown = true;
162 // These should return "true" if the mouse was visible before
163 // the call.
164 if ( event == "Mouse.hide" ) {
165 bool state = mouseShown;
166 mouseShown = false;
167 return state ? "true" : "false";
170 if ( event == "Mouse.show" ) {
171 bool state = mouseShown;
172 mouseShown = true;
173 return state ? "true" : "false" ;
176 // Some fake values for consistent test results.
178 if ( event == "System.capabilities.screenResolutionX" ) {
179 return "800";
182 if ( event == "System.capabilities.screenResolutionY" ) {
183 return "640";
186 if ( event == "System.capabilities.screenDPI" ) {
187 return "72";
190 if ( event == "System.capabilities.screenColor" ) {
191 return "Color";
194 if ( event == "System.capabilities.playerType" ) {
195 return "StandAlone";
198 return "";
202 bool yesNo(const std::string& /*query*/)
204 return true;
208 EventCallback eventCallback;
209 FsCommandExecutor execFsCommand;
212 vm_main(int argc, char *argv[])
214 // Enable native language support, i.e. internationalization
215 #ifdef ENABLE_NLS
216 std::setlocale (LC_ALL, "");
217 bindtextdomain (PACKAGE, LOCALEDIR);
218 textdomain (PACKAGE);
219 #endif
220 int c;
222 // scan for the two main standard GNU options
223 for (c = 0; c < argc; c++) {
224 if (strcmp("--help", argv[c]) == 0) {
225 exit(EXIT_SUCCESS);
227 if (strcmp("--version", argv[c]) == 0) {
228 printf (_("Gnash gprocessor version: %s, Gnash version: %s\n"),
229 GPROC_VERSION, VERSION);
230 exit(EXIT_SUCCESS);
234 std::vector<std::string> infiles;
236 //RcInitFile& rcfile = RcInitFile::getDefaultInstance();
237 //rcfile.loadFiles();
239 if (rcfile.verbosityLevel() > 0) {
240 dbglogfile.setVerbosity(rcfile.verbosityLevel());
243 dbglogfile.setLogFilename(rcfile.getDebugLog());
245 if (rcfile.useWriteLog()) {
246 dbglogfile.setWriteDisk(true);
249 if (rcfile.useActionDump()) {
250 dbglogfile.setActionDump(true);
251 dbglogfile.setVerbosity();
254 if (rcfile.useParserDump()) {
255 dbglogfile.setParserDump(true);
256 dbglogfile.setVerbosity();
259 while ((c = getopt (argc, argv, ":hwvapr:gf:d:")) != -1) {
260 switch (c) {
261 case 'h':
262 dbglogfile.removeLog();
263 exit(EXIT_SUCCESS);
264 case 'w':
265 s_do_output = true;
266 break;
267 case 'v':
268 dbglogfile.setVerbosity();
269 log_debug (_("Verbose output turned on"));
270 break;
271 case 'g':
272 #ifdef USE_DEBUGGER
273 debugger.enabled(true);
274 debugger.console();
275 log_debug (_("Setting debugger ON"));
276 #else
277 log_error (_("The debugger has been disabled at configuration time"));
278 #endif
279 case 'a':
280 #if VERBOSE_ACTION
281 dbglogfile.setActionDump(true);
282 #else
283 log_error (_("Verbose actions disabled at compile time"));
284 #endif
285 break;
286 case 'p':
287 #if VERBOSE_PARSE
288 dbglogfile.setParserDump(true);
289 #else
290 log_error (_("Verbose parsing disabled at compile time"));
291 #endif
292 break;
293 case 'r':
294 allowed_end_hits = strtol(optarg, NULL, 0);
295 break;
296 case 'd':
297 delay = strtol(optarg, NULL, 0)*1000; // delay is in microseconds
298 // this will be recognized as a request to run at FPS speed
299 if ( delay < 0 ) delay = -1;
300 break;
301 case 'f':
302 limit_advances = strtol(optarg, NULL, 0);
303 break;
304 case ':':
305 fprintf(stderr, "Missing argument for switch ``%c''\n", optopt);
306 exit(EXIT_FAILURE);
307 case '?':
308 default:
309 fprintf(stderr, "Unknown switch ``%c''\n", optopt);
310 exit(EXIT_FAILURE);
315 // get the file name from the command line
316 while (optind < argc) {
317 infiles.push_back(argv[optind]);
318 optind++;
321 // No file names were supplied
322 if (infiles.empty()) {
323 std::cerr << "no input files" << std::endl;
324 dbglogfile.removeLog();
325 exit(EXIT_FAILURE);
328 if (infiles.size() > 1) {
329 // We're not ready for multiple runs yet.
330 std::cerr << "Multiple input files not supported." << std::endl;
331 dbglogfile.removeLog();
332 exit(EXIT_FAILURE);
336 #ifdef USE_FFMPEG
337 std::auto_ptr<media::MediaHandler> handler(
338 new gnash::media::ffmpeg::MediaHandlerFfmpeg() );
339 #elif defined(USE_GST)
340 std::auto_ptr<media::MediaHandler> handler(
341 new gnash::media::gst::MediaHandlerGst() );
342 #else
343 std::cerr << "Neither SOUND_SDL nor SOUND_GST defined" << std::endl;
344 exit(EXIT_FAILURE);
345 #endif
346 gnash::media::MediaHandler::set(handler);
348 boost::shared_ptr<sound::sound_handler> soundHandler(
349 new sound::NullSoundHandler());
351 std::vector<movie_data> data;
353 // Play through all the movies.
354 for (std::vector<std::string>::const_iterator i = infiles.begin(),
355 e = infiles.end(); i != e; ++i) {
357 RunResources runResources(*i);
358 runResources.setSoundHandler(soundHandler);
360 boost::intrusive_ptr<gnash::movie_definition> m =
361 play_movie(*i, runResources);
362 if (!m) {
363 if (s_stop_on_errors) {
364 // Fail.
365 std::cerr << "error playing through movie " << *i << std::endl;
366 std::exit(EXIT_FAILURE);
370 movie_data md;
371 md.m_movie = m.get();
372 md.m_filename = *i;
373 data.push_back(md);
377 return 0;
380 // Load the named movie, make an instance, and play it, virtually.
381 // I.e. run through and render all the frames, even though we are not
382 // actually doing any output (our output handlers are disabled).
384 // What this does is warm up all the cached data in the movie, so that
385 // if we save that data for later, we won't have to tesselate shapes
386 // or build font textures again.
388 // Return the movie definition.
389 boost::intrusive_ptr<gnash::movie_definition>
390 play_movie(const std::string& filename, const RunResources& runResources)
392 boost::intrusive_ptr<gnash::movie_definition> md;
394 URL url(filename);
396 try {
397 if (filename == "-") {
398 std::auto_ptr<IOChannel> in (
399 noseek_fd_adapter::make_stream(fileno(stdin)) );
400 md = gnash::MovieFactory::makeMovie(in, filename, runResources, false);
401 } else {
402 if ( url.protocol() == "file" ) {
403 const std::string& path = url.path();
404 #if 1 // add the *directory* the movie was loaded from to the local sandbox path
405 size_t lastSlash = path.find_last_of('/');
406 std::string dir = path.substr(0, lastSlash+1);
407 rcfile.addLocalSandboxPath(dir);
408 log_debug(_("%s appended to local sandboxes"), dir.c_str());
409 #else // add the *file* to be loaded to the local sandbox path
410 rcfile.addLocalSandboxPath(path);
411 log_debug(_("%s appended to local sandboxes"), path.c_str());
412 #endif
414 md = gnash::MovieFactory::makeMovie(url, runResources, NULL, false);
417 catch (GnashException& ge) {
418 md = NULL;
419 fprintf(stderr, "%s\n", ge.what());
421 if (md == NULL) {
422 std::cerr << "error: can't play movie: "<< filename << std::endl;
423 std::exit(EXIT_FAILURE);
426 float fps = md->get_frame_rate();
427 long fpsDelay = long(1000000/fps);
428 long clockAdvance = fpsDelay/1000;
429 long localDelay = delay == -1 ? fpsDelay : delay; // microseconds
431 log_debug("Will sleep %ld microseconds between iterations - "
432 "fps is %g, clockAdvance is %lu", localDelay, fps, clockAdvance);
435 // Use a clock advanced at every iteration to match exact FPS speed.
436 ManualClock cl;
437 gnash::movie_root m(*md, cl, runResources);
439 // Register processor to receive ActionScript events (Mouse, Stage
440 // System etc).
441 m.registerEventCallback(&eventCallback);
442 m.registerFSCommandCallback(&execFsCommand);
444 md->completeLoad();
446 m.init(md.get(), MovieClip::MovieVariables(), MovieClip::MovieVariables());
447 if ( quitrequested ) { // setRootMovie would execute actions in first frame
448 quitrequested = false;
449 return md;
452 log_debug("iteration, timer: %lu, localDelay: %ld\n",
453 cl.elapsed(), localDelay);
454 gnashSleep(localDelay);
456 resetLastAdvanceTimer();
457 int kick_count = 0;
458 int stop_count=0;
459 size_t loop_back_count=0;
460 size_t latest_frame=0;
461 size_t end_hitcount=0;
462 size_t nadvances=0;
463 // Run through the movie.
464 for (;;) {
465 // @@ do we also have to run through all sprite frames
466 // as well?
468 // @@ also, ActionScript can rescale things
469 // dynamically -- we can't really do much about that I
470 // guess?
472 // @@ Maybe we should allow the user to specify some
473 // safety margin on scaled shapes.
475 size_t last_frame = m.get_current_frame();
476 //printf("advancing clock by %lu\n", clockAdvance);
477 cl.advance(clockAdvance);
478 m.advance();
480 if ( quitrequested )
482 quitrequested = false;
483 return md;
486 m.display(); // FIXME: for which reason are we calling display here ??
487 ++nadvances;
488 if ( limit_advances && nadvances >= limit_advances) {
489 log_debug("exiting after %d advances", nadvances);
490 break;
493 size_t curr_frame = m.get_current_frame();
495 // We reached the end, done !
496 if (curr_frame >= md->get_frame_count() - 1 ) {
497 if ( allowed_end_hits && ++end_hitcount >= allowed_end_hits )
499 log_debug("exiting after %d"
500 " times last frame was reached", end_hitcount);
501 break;
505 // We didn't advance
506 if (curr_frame == last_frame) {
507 // Max stop counts reached, kick it
508 if ( secondsSinceLastAdvance() > waitforadvance ) {
509 stop_count=0;
511 // Kick the movie.
512 if ( last_frame + 1 > md->get_frame_count() -1 ) {
513 fprintf(stderr, "Exiting after %g seconds in STOP mode at last frame\n", waitforadvance);
514 break;
516 fprintf(stderr, "Kicking movie after %g seconds in STOP mode, kick ct = %d\n", waitforadvance, kick_count);
517 fflush(stderr);
518 m.goto_frame(last_frame + 1);
519 m.getRootMovie().setPlayState(gnash::MovieClip::PLAYSTATE_PLAY);
520 kick_count++;
522 if (kick_count > 10) {
523 printf("movie is stalled; giving up on playing it through.\n");
524 break;
527 resetLastAdvanceTimer(); // It's like we advanced
531 // We looped back. Skip ahead...
532 else if (m.get_current_frame() < last_frame) {
533 if ( last_frame > latest_frame ) latest_frame = last_frame;
534 if ( ++loop_back_count > allowloopbacks ) {
535 log_debug("%d loop backs; jumping one-after "
536 "latest frame (%d)",
537 loop_back_count, latest_frame+1);
538 m.goto_frame(latest_frame + 1);
539 loop_back_count = 0;
541 } else {
542 kick_count = 0;
543 stop_count = 0;
544 resetLastAdvanceTimer();
547 log_debug("iteration, timer: %lu, localDelay: %ld\n",
548 cl.elapsed(), localDelay);
549 gnashSleep(localDelay);
552 // Clear resources.
553 MovieFactory::clear();
555 return md;
560 // Local Variables:
561 // mode: C++
562 // indent-tabs-mode: t
563 // End: