enable docbook, don't gzip man pages
[gnash.git] / gui / gui.cpp
blob61cc5c944d0530e9aee0ad39e3734d43ae610af1
1 // gui.cpp: Top level GUI for SWF player, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
4 // 2011 Free Software Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h"
23 #endif
25 #include "gui.h"
27 #include <vector>
28 #include <algorithm>
29 #include <boost/assign/list_of.hpp>
31 #include "MovieClip.h"
32 #include "Renderer.h"
33 #include "sound_handler.h"
34 #include "movie_root.h"
35 #include "VM.h"
36 #include "DisplayObject.h"
37 #include "GnashEnums.h"
38 #include "RunResources.h"
39 #include "StreamProvider.h"
40 #include "ScreenShotter.h"
42 #ifdef GNASH_FPS_DEBUG
43 #include "ClockTime.h"
44 #include <boost/format.hpp>
45 #endif
47 /// Define this to make sure each frame is fully rendered from ground up
48 /// even if no motion has been detected in the movie.
49 //#define FORCE_REDRAW 1
51 /// Define this if you want to debug the *detection* of region updates only.
52 /// This will disable region updates for the backend (GUI+renderer) completely
53 /// so that only the last region (red frame) will be visible. However, this
54 /// slows down rendering as each frame is fully re-rendered. If you want to
55 /// debug the GUI part, however (see if blitting the region works), then you
56 /// probably won't define this.
57 #ifndef DISABLE_REGION_UPDATES_DEBUGGING
58 //#define REGION_UPDATES_DEBUGGING_FULL_REDRAW 1
59 #endif
61 #ifndef DISABLE_REGION_UPDATES_DEBUGGING
62 // a runtime check would make the { x; } block conditionally executed
63 #define IF_DEBUG_REGION_UPDATES(x) { if (_showUpdatedRegions) { x } }
64 #else
65 #define IF_DEBUG_REGION_UPDATES(x)
66 #endif
68 // Define this to have gnash print the mouse pointer coordinates
69 // as the mouse moves. See also ENABLE_KEYBOARD_MOUSE_MOVEMENTS
70 // to have more control over mouse pointer.
72 //#define DEBUG_MOUSE_COORDINATES 1
74 namespace gnash {
76 struct Gui::Display
78 Display(Gui& g, movie_root& r) : _g(g), _r(r) {}
79 void operator()() const {
80 InvalidatedRanges world_ranges;
81 world_ranges.setWorld();
82 _g.setInvalidatedRegions(world_ranges);
83 _g.display(&_r);
85 private:
86 Gui& _g;
87 movie_root& _r;
90 Gui::Gui(RunResources& r) :
91 _loop(true),
92 _xid(0),
93 _width(1),
94 _height(1),
95 _runResources(r),
96 _interval(0),
97 _redraw_flag(true),
98 _fullscreen(false),
99 _mouseShown(true),
100 _maxAdvances(0),
101 _advances(0),
102 _xscale(1.0f),
103 _yscale(1.0f),
104 _xoffset(0),
105 _yoffset(0)
106 #ifdef GNASH_FPS_DEBUG
107 ,fps_counter(0)
108 ,fps_counter_total(0)
109 ,fps_timer(0)
110 ,fps_timer_interval(0.0)
111 ,frames_dropped(0)
112 #endif
113 ,_movieDef(0)
114 ,_stage(0)
115 ,_stopped(false)
116 ,_started(false)
117 ,_showUpdatedRegions(false)
119 // NOTE: it's important that _systemClock is constructed
120 // before and destroyed after _virtualClock !
121 ,_systemClock()
122 ,_virtualClock(_systemClock)
123 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
124 ,_xpointer(0)
125 ,_ypointer(0)
126 ,_keyboardMouseMovements(true) // TODO: base default on gnashrc or always false and provide menu item to toggle
127 ,_keyboardMouseMovementsStep(1)
128 #endif
133 Gui::Gui(unsigned long xid, float scale, bool loop, RunResources& r)
135 _loop(loop),
136 _xid(xid),
137 _width(1),
138 _height(1),
139 _runResources(r),
140 _interval(0),
141 _redraw_flag(true),
142 _fullscreen(false),
143 _mouseShown(true),
144 _maxAdvances(0),
145 _advances(0),
146 _xscale(scale),
147 _yscale(scale),
148 _xoffset(0), // TODO: x and y offset will need update !
149 _yoffset(0)
150 #ifdef GNASH_FPS_DEBUG
151 ,fps_counter(0)
152 ,fps_counter_total(0)
153 ,fps_timer(0)
154 ,fps_timer_interval(0.0)
155 ,frames_dropped(0)
156 #endif
157 ,_movieDef(0)
158 ,_stage(0)
159 ,_stopped(false)
160 ,_started(false)
161 ,_showUpdatedRegions(false)
163 // NOTE: it's important that _systemClock is constructed
164 // before and destroyed after _virtualClock !
165 ,_systemClock()
166 ,_virtualClock(_systemClock)
167 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
168 ,_xpointer(0)
169 ,_ypointer(0)
170 ,_keyboardMouseMovements(true) // TODO: base default on gnashrc or always false and provide menu item to toggle
171 ,_keyboardMouseMovementsStep(1)
172 #endif
176 Gui::~Gui()
178 if ( _movieDef.get() ) {
179 log_debug("~Gui - _movieDef refcount: %d", _movieDef->get_ref_count());
182 #ifdef GNASH_FPS_DEBUG
183 if ( fps_timer_interval ) {
184 std::cerr << "Total frame advances/drops: "
185 << fps_counter_total << "/" << frames_dropped << std::endl;
187 #endif
190 void
191 Gui::setClipboard(const std::string&)
193 LOG_ONCE(log_unimpl(_("Clipboard not yet supported in this GUI")));
196 void
197 Gui::setFullscreen()
199 log_unimpl(_("Fullscreen not yet supported in this GUI"));
202 void
203 Gui::resizeWindow(int /*width*/, int /*height*/)
205 log_unimpl(_("Window resize not yet supported in this GUI"));
208 void
209 Gui::unsetFullscreen()
211 log_unimpl(_("Fullscreen not yet supported in this GUI"));
214 void
215 Gui::quit()
217 // Take a screenshot of the last frame if required.
218 if (_screenShotter.get() && _renderer.get()) {
219 Display dis(*this, *_stage);
220 _screenShotter->last(*_renderer, &dis);
223 quitUI();
226 void
227 Gui::hideMenu()
229 LOG_ONCE(log_unimpl(_("Menu show/hide not yet supported in this GUI")));
232 bool
233 Gui::showMouse(bool /* show */)
235 LOG_ONCE(log_unimpl(_("Mouse show/hide not yet supported in this GUI")));
236 return true;
239 void
240 Gui::showMenu(bool /* show */)
242 LOG_ONCE(log_unimpl(_("Menu show/hide not yet supported in this GUI")));
245 void
246 Gui::allowScale(bool allow)
248 if (!_stage) {
249 log_error("Gui::allowScale called before a movie_root was available");
250 return;
253 if (allow) _stage->setStageScaleMode(movie_root::SCALEMODE_SHOWALL);
254 else _stage->setStageScaleMode(movie_root::SCALEMODE_NOSCALE);
257 void
258 Gui::toggleFullscreen()
260 /// Sends request to Gnash core to change display state.
261 if (_fullscreen) {
262 _stage->setStageDisplayState(movie_root::DISPLAYSTATE_NORMAL);
263 } else {
264 _stage->setStageDisplayState(movie_root::DISPLAYSTATE_FULLSCREEN);
268 void
269 Gui::restart()
271 _stage->reset();
272 _started = false;
273 start();
276 void
277 Gui::updateStageMatrix()
279 if (!_stage) {
280 // When VM initializes, we'll get a call to resize_view, which
281 // would call us again.
282 log_error(_("Can't update stage matrix till VM is initialized"));
283 return;
286 assert(_stage); // when VM is initialized this should hold
288 float swfwidth = _movieDef->get_width_pixels();
289 float swfheight = _movieDef->get_height_pixels();
291 // Fetch scale mode
292 movie_root::ScaleMode scaleMode = _stage->getStageScaleMode();
294 switch (scaleMode) {
295 case movie_root::SCALEMODE_NOSCALE:
296 _xscale = _yscale = 1.0f;
297 break;
299 case movie_root::SCALEMODE_SHOWALL:
300 // set new scale value ( user-pixel / pseudo-pixel ). Do
301 // not divide by zero, or we end up with an invalid
302 // stage matrix that returns nan values.
303 _xscale = (swfwidth == 0.0f) ? 1.0f : _width / swfwidth;
304 _yscale = (swfheight == 0.0f) ? 1.0f : _height / swfheight;
306 // Scale proportionally, using smallest scale
307 if (_xscale < _yscale) {
308 _yscale = _xscale;
309 } else if (_yscale < _xscale) {
310 _xscale = _yscale;
312 break;
314 case movie_root::SCALEMODE_NOBORDER:
315 // set new scale value ( user-pixel / pseudo-pixel )
316 _xscale = (swfwidth == 0.0f) ? 1.0f : _width / swfwidth;
317 _yscale = (swfheight == 0.0f) ? 1.0f : _height / swfheight;
319 // Scale proportionally, using biggest scale
320 if (_xscale > _yscale) {
321 _yscale = _xscale;
322 } else if (_yscale > _xscale) {
323 _xscale = _yscale;
325 break;
327 case movie_root::SCALEMODE_EXACTFIT:
328 // NOTE: changing aspect ratio is valid!
329 _xscale = (swfwidth == 0.0f) ? 1.0f : _width / swfwidth;
330 _yscale = (swfheight == 0.0f) ? 1.0f : _height / swfheight;
331 break;
333 default:
334 log_error(_("Invalid scaleMode %d"), scaleMode);
335 break;
338 _xoffset=0;
339 _yoffset=0;
341 // Fetch align mode
342 movie_root::StageAlign align = _stage->getStageAlignment();
343 movie_root::StageHorizontalAlign halign = align.first;
344 movie_root::StageVerticalAlign valign = align.second;
346 // Handle horizontal alignment
347 switch ( halign ) {
348 case movie_root::STAGE_H_ALIGN_L:
350 // _xoffset=0 is fine
351 break;
354 case movie_root::STAGE_H_ALIGN_R:
356 // Offsets in pixels
357 float defWidth = swfwidth *= _xscale;
358 float diffWidth = _width-defWidth;
359 _xoffset = diffWidth;
360 break;
363 case movie_root::STAGE_V_ALIGN_C:
365 // Offsets in pixels
366 float defWidth = swfwidth *= _xscale;
367 float diffWidth = _width-defWidth;
368 _xoffset = diffWidth/2.0;
369 break;
372 default:
374 log_error(_("Invalid horizontal align %d"), valign);
375 break;
379 // Handle vertical alignment
380 switch ( valign ) {
381 case movie_root::STAGE_V_ALIGN_T:
383 // _yoffset=0 is fine
384 break;
387 case movie_root::STAGE_V_ALIGN_B:
389 float defHeight = swfheight *= _yscale;
390 float diffHeight = _height-defHeight;
391 _yoffset = diffHeight;
392 break;
395 case movie_root::STAGE_V_ALIGN_C:
397 float defHeight = swfheight *= _yscale;
398 float diffHeight = _height-defHeight;
399 _yoffset = diffHeight/2.0;
400 break;
403 default:
405 log_error(_("Invalid vertical align %d"), valign);
406 break;
410 //log_debug("updateStageMatrix: scaleMode:%d, valign:%d, halign:%d",
411 //scaleMode, valign, halign);
413 // TODO: have a generic set_matrix ?
414 if (_renderer.get()) {
415 _renderer->set_scale(_xscale, _yscale);
416 _renderer->set_translation(_xoffset, _yoffset);
417 } else {
418 //log_debug("updateStageMatrix: could not signal updated stage
419 //matrix to renderer (no renderer registered)");
422 // trigger redraw
423 //_redraw_flag |= (_width!=width) || (_height!=height);
424 _redraw_flag = true; // this fixes bug #21971
428 void
429 Gui::resize_view(int width, int height)
431 GNASH_REPORT_FUNCTION;
433 assert(width > 0);
434 assert(height > 0);
436 if (_stage && _started) {
437 _stage->setDimensions(width, height);
440 _width = width;
441 _height = height;
442 _validbounds.setTo(0, 0, _width, _height);
444 updateStageMatrix();
446 if ( _stage && _started ) {
447 display(_stage);
452 void
453 Gui::toggleSound()
455 assert (_stage);
456 // @todo since we registered the sound handler, shouldn't we know
457 // already what it is ?!
458 sound::sound_handler* s = _stage->runResources().soundHandler();
460 if (!s) return;
462 if (s->is_muted()) s->unmute();
463 else s->mute();
467 void
468 Gui::notifyMouseMove(int ux, int uy)
470 movie_root* m = _stage;
472 if ( ! _started ) return;
474 if ( _stopped ) return;
476 // A stage pseudopixel is user pixel / _xscale wide
477 boost::int32_t x = (ux-_xoffset) / _xscale;
479 // A stage pseudopixel is user pixel / _xscale high
480 boost::int32_t y = (uy-_yoffset) / _yscale;
482 #ifdef DEBUG_MOUSE_COORDINATES
483 log_debug("mouse @ %d,%d", x, y);
484 #endif
486 if ( m->mouseMoved(x, y) ) {
487 // any action triggered by the
488 // event required screen refresh
489 display(m);
492 DisplayObject* activeEntity = m->getActiveEntityUnderPointer();
493 if ( activeEntity ) {
494 if ( activeEntity->isSelectableTextField() ) {
495 setCursor(CURSOR_INPUT);
496 } else if ( activeEntity->allowHandCursor() ) {
497 setCursor(CURSOR_HAND);
498 } else {
499 setCursor(CURSOR_NORMAL);
501 } else {
502 setCursor(CURSOR_NORMAL);
505 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
506 _xpointer = ux;
507 _ypointer = uy;
508 #endif
513 void
514 Gui::notifyMouseWheel(int delta)
516 movie_root* m = _stage;
517 assert(m);
519 if (!_started) return;
520 if (_stopped) return;
522 if (m->mouseWheel(delta)) {
523 // any action triggered by the
524 // event required screen refresh
525 display(m);
529 void
530 Gui::notifyMouseClick(bool mouse_pressed)
532 movie_root* m = _stage;
533 assert(m);
535 if (!_started) return;
536 if (_stopped) return;
538 if (m->mouseClick(mouse_pressed)) {
539 // any action triggered by the
540 // event required screen refresh
541 display(m);
545 void
546 Gui::refreshView()
548 movie_root* m = _stage;
550 if ( ! _started ) return;
552 assert(m);
553 _redraw_flag=true;
554 display(m);
558 void
559 Gui::notify_key_event(gnash::key::code k, int modifier, bool pressed)
562 // Handle GUI shortcuts
563 if (pressed) {
564 if (k == gnash::key::ESCAPE) {
565 if (isFullscreen()) {
566 _stage->setStageDisplayState(movie_root::DISPLAYSTATE_NORMAL);
570 if (modifier & gnash::key::GNASH_MOD_CONTROL) {
571 switch (k) {
572 case gnash::key::o:
573 case gnash::key::O:
574 takeScreenShot();
575 break;
576 case gnash::key::r:
577 case gnash::key::R:
578 restart();
579 break;
580 case gnash::key::p:
581 case gnash::key::P:
582 pause();
583 break;
584 case gnash::key::l:
585 case gnash::key::L:
586 refreshView();
587 break;
588 case gnash::key::q:
589 case gnash::key::Q:
590 case gnash::key::w:
591 case gnash::key::W:
592 quit();
593 break;
594 case gnash::key::f:
595 case gnash::key::F:
596 toggleFullscreen();
597 break;
598 case gnash::key::h:
599 case gnash::key::H:
600 showUpdatedRegions(!showUpdatedRegions());
601 break;
602 case gnash::key::MINUS:
604 // Max interval allowed: 1 second (1FPS)
605 const size_t ni = std::min<size_t>(_interval + 2, 1000u);
606 setInterval(ni);
607 break;
609 case gnash::key::PLUS:
611 // Min interval allowed: 1/100 second (100FPS)
612 const size_t ni = std::max<size_t>(_interval - 2, 10u);
613 setInterval(ni);
614 break;
616 case gnash::key::EQUALS:
618 if (_stage) {
619 const float fps = _stage->getRootMovie().frameRate();
620 // Min interval allowed: 1/100 second (100FPS)
621 const size_t ni = 1000.0/fps;
622 setInterval(ni);
624 break;
626 default:
627 break;
630 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
631 if ( _keyboardMouseMovements ) {
632 int step = _keyboardMouseMovementsStep;
633 // x5 if SHIFT is pressed
634 if (modifier & gnash::key::GNASH_MOD_SHIFT) step *= 5;
635 switch (k) {
636 case gnash::key::UP:
638 int newx = _xpointer;
639 int newy = _ypointer-step;
640 if ( newy < 0 ) newy=0;
641 notifyMouseMove(newx, newy);
642 break;
644 case gnash::key::DOWN:
646 int newx = _xpointer;
647 int newy = _ypointer+step;
648 if ( newy >= _height ) newy = _height-1;
649 notifyMouseMove(newx, newy);
650 break;
652 case gnash::key::LEFT:
654 int newx = _xpointer-step;
655 int newy = _ypointer;
656 if ( newx < 0 ) newx = 0;
657 notifyMouseMove(newx, newy);
658 break;
660 case gnash::key::RIGHT:
662 const int newy = _ypointer;
663 int newx = _xpointer + step;
664 if ( newx >= _width ) newx = _width-1;
665 notifyMouseMove(newx, newy);
666 break;
668 default:
669 break;
672 #endif // ENABLE_KEYBOARD_MOUSE_MOVEMENTS
676 if (!_started) return;
678 if (_stopped) return;
680 if (_stage->keyEvent(k, pressed)) {
681 // any action triggered by the
682 // event required screen refresh
683 display(_stage);
688 bool
689 Gui::display(movie_root* m)
691 assert(m == _stage); // why taking this arg ??
693 assert(_started);
695 InvalidatedRanges changed_ranges;
696 bool redraw_flag;
698 // Should the frame be rendered completely, even if it did not change?
699 #ifdef FORCE_REDRAW
700 redraw_flag = true;
701 #else
702 redraw_flag = _redraw_flag || want_redraw();
703 #endif
705 // reset class member if we do a redraw now
706 if (redraw_flag) _redraw_flag=false;
708 // Find out the surrounding frame of all characters which
709 // have been updated. This just checks what region of the stage has changed
710 // due to ActionScript code, the timeline or user events. The GUI can still
711 // choose to render a different part of the stage.
713 if (!redraw_flag) {
715 // choose snapping ranges factor
716 changed_ranges.setSnapFactor(1.3f);
718 // Use multi ranges only when GUI/Renderer supports it
719 // (Useless CPU overhead, otherwise)
720 changed_ranges.setSingleMode(!want_multiple_regions());
722 // scan through all sprites to compute invalidated bounds
723 m->add_invalidated_bounds(changed_ranges, false);
725 // grow ranges by a 2 pixels to avoid anti-aliasing issues
726 changed_ranges.growBy(40.0f / _xscale);
728 // optimize ranges
729 changed_ranges.combineRanges();
732 // TODO: Remove this and want_redraw to avoid confusion!?
733 if (redraw_flag) {
734 changed_ranges.setWorld();
737 // DEBUG ONLY:
738 // This is a good place to inspect the invalidated bounds state. Enable
739 // the following block (and parts of it) if you need to.
740 #if 0
742 // This may print a huge amount of information, but is useful to analyze
743 // the (visible) object structure of the movie and the flags of the
744 // characters. For example, a characters should have set the
745 // m_child_invalidated flag if at least one of it's childs has the
746 // invalidated flag set.
747 log_debug("DUMPING CHARACTER TREE");
749 InfoTree tr;
750 InfoTree::iterator top = tr.begin();
751 _stage->getMovieInfo(tr, top);
753 for (InfoTree::iterator i = tr.begin(), e = tr.end();
754 i != e; ++i) {
755 std::cout << std::string(tr.depth(i) * 2, ' ') << i->first << ": " <<
756 i->second << std::endl;
760 // less verbose, and often necessary: see the exact coordinates of the
761 // invalidated bounds (mainly to see if it's NULL or something else).
762 std::cout << "Calculated changed ranges: " << changed_ranges << "\n";
764 #endif
766 // Avoid drawing of stopped movies
767 if ( ! changed_ranges.isNull() ) { // use 'else'?
768 // Tell the GUI(!) that we only need to update this
769 // region. Note the GUI can do whatever it wants with
770 // this information. It may simply ignore the bounds
771 // (which will normally lead into a complete redraw),
772 // or it may extend or shrink the bounds as it likes. So,
773 // by calling set_invalidated_bounds we have no guarantee
774 // that only this part of the stage is rendered again.
775 #ifdef REGION_UPDATES_DEBUGGING_FULL_REDRAW
776 // redraw the full screen so that only the
777 // *new* invalidated region is visible
778 // (helps debugging)
779 InvalidatedRanges world_ranges;
780 world_ranges.setWorld();
781 setInvalidatedRegions(world_ranges);
782 #else
783 setInvalidatedRegions(changed_ranges);
784 #endif
786 // TODO: should this be called even if we're late ?
787 beforeRendering();
789 // Render the frame, if not late.
790 // It's up to the GUI/renderer combination
791 // to do any clipping, if desired.
792 m->display();
794 // show invalidated region using a red rectangle
795 // (Flash debug style)
796 IF_DEBUG_REGION_UPDATES (
797 if (_renderer.get() && !changed_ranges.isWorld()) {
798 for (size_t rno = 0; rno < changed_ranges.size(); rno++) {
799 const geometry::Range2d<int>& bounds =
800 changed_ranges.getRange(rno);
802 float xmin = bounds.getMinX();
803 float xmax = bounds.getMaxX();
804 float ymin = bounds.getMinY();
805 float ymax = bounds.getMaxY();
807 const std::vector<point> box = boost::assign::list_of
808 (point(xmin, ymin))
809 (point(xmax, ymin))
810 (point(xmax, ymax))
811 (point(xmin, ymax));
813 _renderer->draw_poly(box, rgba(0,0,0,0), rgba(255,0,0,255),
814 SWFMatrix(), false);
820 // show frame on screen
821 renderBuffer();
824 return true;
827 void
828 Gui::play()
830 if ( ! _stopped ) return;
832 _stopped = false;
833 if ( ! _started ) {
834 start();
835 } else {
836 assert (_stage);
837 // @todo since we registered the sound handler, shouldn't we know
838 // already what it is ?!
839 sound::sound_handler* s = _stage->runResources().soundHandler();
840 if ( s ) s->unpause();
842 // log_debug("Starting virtual clock");
843 _virtualClock.resume();
846 playHook ();
849 void
850 Gui::stop()
852 // _stage must be registered before this is called.
853 assert(_stage);
855 if ( _stopped ) return;
856 if ( isFullscreen() ) unsetFullscreen();
858 _stopped = true;
860 // @todo since we registered the sound handler, shouldn't we know
861 // already what it is ?!
862 sound::sound_handler* s = _stage->runResources().soundHandler();
863 if ( s ) s->pause();
865 // log_debug("Pausing virtual clock");
866 _virtualClock.pause();
868 stopHook();
871 void
872 Gui::pause()
874 if (_stopped) {
875 play();
876 return;
879 // TODO: call stop() instead ?
880 // The only thing I see is that ::stop exits full-screen,
881 // but I'm not sure that's intended behaviour
883 // @todo since we registered the sound handler, shouldn't we know
884 // already what it is ?!
885 sound::sound_handler* s = _stage->runResources().soundHandler();
886 if (s) s->pause();
887 _stopped = true;
889 // log_debug("Pausing virtual clock");
890 _virtualClock.pause();
892 stopHook();
895 void
896 Gui::start()
898 assert ( ! _started );
899 if (_stopped) {
900 log_error(_("Gui is in stop mode, won't start application"));
901 return;
904 // Initializes the stage with a Movie and the passed flash vars.
905 _stage->init(_movieDef.get(), _flashVars);
907 bool background = true; // ??
908 _stage->set_background_alpha(background ? 1.0f : 0.05f);
910 // to properly update stageMatrix if scaling is given
911 resize_view(_width, _height);
913 // @todo since we registered the sound handler, shouldn't we know
914 // already what it is ?!
915 sound::sound_handler* s = _stage->runResources().soundHandler();
916 if ( s ) {
917 if ( ! _audioDump.empty() ) {
918 s->setAudioDump(_audioDump);
920 s->unpause();
922 _started = true;
924 // log_debug("Starting virtual clock");
925 _virtualClock.resume();
929 bool
930 Gui::advanceMovie(bool doDisplay)
932 if (isStopped()) {
933 return false;
936 if (!_started) {
937 start();
940 Display dis(*this, *_stage);
941 gnash::movie_root* m = _stage;
943 // Define REVIEW_ALL_FRAMES to have *all* frames
944 // consequentially displayed. Useful for debugging.
945 //#define REVIEW_ALL_FRAMES 1
947 #ifndef REVIEW_ALL_FRAMES
948 // Advance movie by one frame
949 const bool advanced = m->advance();
950 #else
951 const size_t cur_frame = m->getRootMovie().get_current_frame();
952 const size_t tot_frames = m->getRootMovie().get_frame_count();
953 const bool advanced = m->advance();
955 m->getRootMovie().ensureFrameLoaded(tot_frames);
956 m->goto_frame(cur_frame + 1);
957 m->getRootMovie().setPlayState(gnash::MovieClip::PLAYSTATE_PLAY);
958 // log_debug("Frame %d", m->getRootMovie().get_current_frame());
959 #endif
961 #ifdef GNASH_FPS_DEBUG
962 // will be a no-op if fps_timer_interval is zero
963 if (advanced) {
964 fpsCounterTick();
966 #endif
968 if (doDisplay && visible()) {
969 display(m);
972 if (!loops()) {
973 // can be 0 on malformed SWF
974 const size_t curframe = m->getRootMovie().get_current_frame();
975 const MovieClip& si = m->getRootMovie();
976 if (curframe + 1 >= si.get_frame_count()) {
977 quit();
981 if (_screenShotter.get() && _renderer.get()) {
982 _screenShotter->screenShot(*_renderer, _advances, doDisplay ? 0 : &dis);
985 // Only increment advances and check for exit condition when we've
986 // really changed frame.
987 if (advanced) {
988 /// Quit if we've reached the frame advance limit.
989 if (_maxAdvances && (_advances > _maxAdvances)) {
990 quit();
992 ++_advances;
995 return advanced;
998 void
999 Gui::setScreenShotter(std::auto_ptr<ScreenShotter> ss)
1001 _screenShotter.reset(ss.release());
1004 void
1005 Gui::takeScreenShot()
1007 if (!_screenShotter.get()) {
1008 // If no ScreenShotter exists, none was requested at startup.
1009 // We use a default filename pattern.
1010 URL url(_runResources.streamProvider().baseURL());
1011 std::string::size_type p = url.path().rfind('/');
1012 const std::string& name = (p == std::string::npos) ? url.path() :
1013 url.path().substr(p + 1);
1014 const std::string& filename = "screenshot-" + name + "-%f";
1015 _screenShotter.reset(new ScreenShotter(filename, GNASH_FILETYPE_PNG));
1017 assert (_screenShotter.get());
1018 _screenShotter->now();
1021 void
1022 Gui::setCursor(gnash_cursor_type /*newcursor*/)
1024 /* do nothing */
1027 bool
1028 Gui::want_redraw()
1030 return false;
1033 void
1034 Gui::setInvalidatedRegion(const SWFRect& /*bounds*/)
1036 /* do nothing */
1039 void
1040 Gui::setInvalidatedRegions(const InvalidatedRanges& ranges)
1042 // fallback to single regions
1043 geometry::Range2d<int> full = ranges.getFullArea();
1045 SWFRect bounds;
1047 if (full.isFinite()) {
1048 bounds = SWFRect(full.getMinX(), full.getMinY(),
1049 full.getMaxX(), full.getMaxY());
1051 else if (full.isWorld()) {
1052 bounds.set_world();
1055 setInvalidatedRegion(bounds);
1058 #ifdef USE_SWFTREE
1060 std::auto_ptr<movie_root::InfoTree>
1061 Gui::getMovieInfo() const
1063 std::auto_ptr<movie_root::InfoTree> tr;
1065 if (!_stage) {
1066 return tr;
1069 tr.reset(new movie_root::InfoTree());
1071 // Top nodes for the tree:
1072 // 1. VM information
1073 // 2. "Stage" information
1074 // 3. ...
1076 movie_root::InfoTree::iterator topIter = tr->begin();
1077 movie_root::InfoTree::iterator firstLevelIter;
1079 VM& vm = _stage->getVM();
1081 std::ostringstream os;
1084 /// VM top level
1086 os << "SWF " << vm.getSWFVersion();
1087 topIter = tr->insert(topIter, std::make_pair("Root SWF version", os.str()));
1089 // This short-cut is to avoid a bug in movie_root's getMovieInfo,
1090 // which relies on the availability of a _rootMovie for doing
1091 // it's work, while we don't set it if we didn't start..
1093 if (! _started) {
1094 topIter = tr->insert(topIter, std::make_pair("Stage properties",
1095 "not constructed yet"));
1096 return tr;
1099 movie_root& stage = vm.getRoot();
1100 stage.getMovieInfo(*tr, topIter);
1103 /// Mouse entities
1105 topIter = tr->insert(topIter, std::make_pair("Mouse Entities", ""));
1107 const DisplayObject* ch;
1108 ch = stage.getActiveEntityUnderPointer();
1109 if (ch) {
1110 std::stringstream ss;
1111 ss << ch->getTarget() << " (" + typeName(*ch)
1112 << " - depth:" << ch->get_depth()
1113 << " - useHandCursor:" << ch->allowHandCursor()
1114 << ")";
1115 firstLevelIter = tr->append_child(topIter,
1116 std::make_pair("Active entity under mouse pointer", ss.str()));
1119 ch = stage.getEntityUnderPointer();
1120 if (ch) {
1121 std::stringstream ss;
1122 ss << ch->getTarget() << " (" + typeName(*ch)
1123 << " - depth:" << ch->get_depth()
1124 << ")";
1125 firstLevelIter = tr->append_child(topIter,
1126 std::make_pair("Topmost entity under mouse pointer", ss.str()));
1129 ch = stage.getDraggingCharacter();
1130 if (ch) {
1131 std::stringstream ss;
1132 ss << ch->getTarget() << " (" + typeName(*ch)
1133 << " - depth:" << ch->get_depth() << ")";
1134 firstLevelIter = tr->append_child(topIter,
1135 std::make_pair("Dragging character: ", ss.str()));
1139 /// GC row
1141 topIter = tr->insert(topIter, std::make_pair("GC Statistics", ""));
1142 GC::CollectablesCount cc;
1143 _stage->gc().countCollectables(cc);
1145 const std::string lbl = "GC managed ";
1146 for (GC::CollectablesCount::iterator i=cc.begin(), e=cc.end(); i!=e; ++i) {
1147 const std::string& typ = i->first;
1148 std::ostringstream ss;
1149 ss << i->second;
1150 firstLevelIter = tr->append_child(topIter,
1151 std::make_pair(lbl + typ, ss.str()));
1154 tr->sort(firstLevelIter.begin(), firstLevelIter.end());
1156 return tr;
1159 #endif
1161 #ifdef GNASH_FPS_DEBUG
1162 void
1163 Gui::fpsCounterTick()
1166 // increment this *before* the early return so that
1167 // frame count on exit is still valid
1168 ++fps_counter_total;
1170 if (! fps_timer_interval) {
1171 return;
1174 boost::uint64_t current_timer = clocktime::getTicks();
1176 // TODO: keep fps_timer_interval in milliseconds to avoid the multiplication
1177 // at each fpsCounterTick call...
1178 boost::uint64_t interval_ms = (boost::uint64_t)(fps_timer_interval * 1000.0);
1180 if (fps_counter_total==1) {
1181 fps_timer = current_timer;
1182 fps_start_timer = current_timer;
1185 ++fps_counter;
1187 if (current_timer - fps_timer >= interval_ms) {
1189 float secs = (current_timer - fps_timer) / 1000.0;
1190 float secs_total = (current_timer - fps_start_timer)/1000.0;
1192 float rate = fps_counter/secs;
1194 if (secs > 10000000) {
1195 // the timers are unsigned, so when the clock runs "backwards" it leads
1196 // to a very high difference value. In theory, this should never happen
1197 // with ticks, but it does on my machine (which may have a hw problem?).
1198 std::cerr << "Time glitch detected, need to restart FPS counters, sorry..." << std::endl;
1200 fps_timer = current_timer;
1201 fps_start_timer = current_timer;
1202 fps_counter_total = 0;
1203 fps_counter = 0;
1204 return;
1207 // first FPS message?
1208 if (fps_timer == fps_start_timer) { // they're ints, so we can compare
1209 fps_rate_min = rate;
1210 fps_rate_max = rate;
1211 } else {
1212 fps_rate_min = std::min<float>(fps_rate_min, rate);
1213 fps_rate_max = std::max<float>(fps_rate_max, rate);
1216 float avg = fps_counter_total / secs_total;
1218 //log_debug("Effective frame rate: %0.2f fps", (float)(fps_counter/secs));
1219 std::cerr << boost::format("Effective frame rate: %0.2f fps "
1220 "(min %0.2f, avg %0.2f, max %0.2f, "
1221 "%u frames in %0.1f secs total, "
1222 "dropped %u)") % rate %
1223 fps_rate_min % avg % fps_rate_max %
1224 fps_counter_total % secs_total %
1225 frames_dropped << std::endl;
1227 fps_counter = 0;
1228 fps_timer = current_timer;
1233 #endif
1235 void
1236 Gui::addFlashVars(Gui::VariableMap& from)
1238 for (VariableMap::iterator i=from.begin(), ie=from.end(); i!=ie; ++i) {
1239 _flashVars[i->first] = i->second;
1243 void
1244 Gui::setMovieDefinition(movie_definition* md)
1246 assert(!_movieDef);
1247 _movieDef = md;
1250 void
1251 Gui::setStage(movie_root* stage)
1253 assert(stage);
1254 assert(!_stage);
1255 _stage = stage;
1258 bool
1259 Gui::yesno(const std::string& question)
1261 log_error(_("This gui didn't override 'yesno', assuming 'yes' answer to "
1262 "question: %s"), question);
1263 return true;
1266 void
1267 Gui::setQuality(Quality q)
1269 if (!_stage) {
1270 log_error(_("Gui::setQuality called before a movie_root was available"));
1271 return;
1273 _stage->setQuality(q);
1276 Quality
1277 Gui::getQuality() const
1279 if (!_stage) {
1280 log_error(_("Gui::getQuality called before a movie_root was available"));
1281 // just a guess..
1282 return QUALITY_HIGH;
1284 return _stage->getQuality();
1289 // local Variables:
1290 // mode: C++
1291 // indent-tabs-mode: nil
1292 // End: