1 // gui.cpp: Top level GUI for SWF player, for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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
22 #include "gnashconfig.h"
31 #include <boost/algorithm/string/replace.hpp>
32 #include <boost/lexical_cast.hpp>
34 #include "MovieClip.h"
36 #include "sound_handler.h"
37 #include "movie_root.h"
39 #include "DisplayObject.h"
42 #include "RunResources.h"
43 #include "StreamProvider.h"
45 #ifdef GNASH_FPS_DEBUG
46 #include "ClockTime.h"
47 #include <boost/format.hpp>
50 /// Define this to make sure each frame is fully rendered from ground up
51 /// even if no motion has been detected in the movie.
52 //#define FORCE_REDRAW 1
54 /// Define this if you want to debug the *detection* of region updates only.
55 /// This will disable region updates for the backend (GUI+renderer) completely
56 /// so that only the last region (red frame) will be visible. However, this
57 /// slows down rendering as each frame is fully re-rendered. If you want to
58 /// debug the GUI part, however (see if blitting the region works), then you
59 /// probably won't define this.
60 #ifndef DISABLE_REGION_UPDATES_DEBUGGING
61 //#define REGION_UPDATES_DEBUGGING_FULL_REDRAW 1
64 #ifndef DISABLE_REGION_UPDATES_DEBUGGING
65 // a runtime check would make the { x; } block conditionally executed
66 #define IF_DEBUG_REGION_UPDATES(x) { if (_showUpdatedRegions) { x } }
68 #define IF_DEBUG_REGION_UPDATES(x)
71 // Define this to have gnash print the mouse pointer coordinates
72 // as the mouse moves. See also ENABLE_KEYBOARD_MOUSE_MOVEMENTS
73 // to have more control over mouse pointer.
75 //#define DEBUG_MOUSE_COORDINATES 1
77 // Define the following macro if you want to skip rendering
78 // when late on FPS time.
79 // This is an experimental feature, so it's off by default
80 //#define SKIP_RENDERING_IF_LATE 1
85 Gui::Gui(RunResources
& r
) :
101 #ifdef GNASH_FPS_DEBUG
103 ,fps_counter_total(0)
105 ,fps_timer_interval(0.0)
112 ,_showUpdatedRegions(false)
114 // NOTE: it's important that _systemClock is constructed
115 // before and destroyed after _virtualClock !
117 ,_virtualClock(_systemClock
)
118 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
121 ,_keyboardMouseMovements(true) // TODO: base default on gnashrc or always false and provide menu item to toggle
122 ,_keyboardMouseMovementsStep(1)
128 Gui::Gui(unsigned long xid
, float scale
, bool loop
, RunResources
& r
)
143 _xoffset(0), // TODO: x and y offset will need update !
145 #ifdef GNASH_FPS_DEBUG
147 ,fps_counter_total(0)
149 ,fps_timer_interval(0.0)
156 ,_showUpdatedRegions(false)
158 // NOTE: it's important that _systemClock is constructed
159 // before and destroyed after _virtualClock !
161 ,_virtualClock(_systemClock
)
162 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
165 ,_keyboardMouseMovements(true) // TODO: base default on gnashrc or always false and provide menu item to toggle
166 ,_keyboardMouseMovementsStep(1)
173 if ( _movieDef
.get() ) log_debug("~Gui - _movieDef refcount: %d", _movieDef
->get_ref_count());
175 #ifdef GNASH_FPS_DEBUG
176 if ( fps_timer_interval
) {
177 std::cerr
<< "Total frame advances/drops: "
178 << fps_counter_total
<< "/" << frames_dropped
<< std::endl
;
186 log_unimpl(_("Fullscreen not yet supported in this GUI"));
190 Gui::resizeWindow(int /*width*/, int /*height*/)
192 log_unimpl(_("Window resize not yet supported in this GUI"));
196 Gui::unsetFullscreen()
198 log_unimpl(_("Fullscreen not yet supported in this GUI"));
204 log_debug(__PRETTY_FUNCTION__
);
206 // Take a screenshot of the last frame if required.
207 if (_screenShotter
.get()) {
208 _screenShotter
->last();
217 log_unimpl(_("Menu hiding not yet supported in this GUI"));
221 Gui::showMouse(bool /* show */)
223 LOG_ONCE(log_unimpl(_("Mouse show/hide not yet supported in this GUI")));
228 Gui::showMenu(bool /* show */)
230 LOG_ONCE(log_unimpl(_("menushow not yet supported in this GUI")));
234 Gui::allowScale(bool allow
)
237 log_error("Gui::allowScale called before a movie_root was available");
241 if (allow
) _stage
->setStageScaleMode(movie_root::SCALEMODE_SHOWALL
);
242 else _stage
->setStageScaleMode(movie_root::SCALEMODE_NOSCALE
);
246 Gui::toggleFullscreen()
248 /// Sends request to Gnash core to change display state.
250 _stage
->setStageDisplayState(movie_root::DISPLAYSTATE_NORMAL
);
253 _stage
->setStageDisplayState(movie_root::DISPLAYSTATE_FULLSCREEN
);
266 Gui::updateStageMatrix()
269 // When VM initializes, we'll get a call to resize_view, which
270 // would call us again.
271 //log_debug("Can't update stage matrix till VM is initialized");
275 assert(_stage
); // when VM is initialized this should hold
277 float swfwidth
= _movieDef
->get_width_pixels();
278 float swfheight
= _movieDef
->get_height_pixels();
281 movie_root::ScaleMode scaleMode
= _stage
->getStageScaleMode();
284 case movie_root::SCALEMODE_NOSCALE
:
285 _xscale
= _yscale
= 1.0f
;
288 case movie_root::SCALEMODE_SHOWALL
:
289 // set new scale value ( user-pixel / pseudo-pixel ). Do
290 // not divide by zero, or we end up with an invalid
291 // stage matrix that returns nan values.
292 _xscale
= (swfwidth
== 0.0f
) ? 1.0f
: _width
/ swfwidth
;
293 _yscale
= (swfheight
== 0.0f
) ? 1.0f
: _height
/ swfheight
;
295 // Scale proportionally, using smallest scale
296 if (_xscale
< _yscale
) {
299 else if (_yscale
< _xscale
) {
304 case movie_root::SCALEMODE_NOBORDER
:
305 // set new scale value ( user-pixel / pseudo-pixel )
306 _xscale
= (swfwidth
== 0.0f
) ? 1.0f
: _width
/ swfwidth
;
307 _yscale
= (swfheight
== 0.0f
) ? 1.0f
: _height
/ swfheight
;
309 // Scale proportionally, using biggest scale
310 if (_xscale
> _yscale
) {
313 else if (_yscale
> _xscale
) {
318 case movie_root::SCALEMODE_EXACTFIT
:
319 // NOTE: changing aspect ratio is valid!
320 _xscale
= (swfwidth
== 0.0f
) ? 1.0f
: _width
/ swfwidth
;
321 _yscale
= (swfheight
== 0.0f
) ? 1.0f
: _height
/ swfheight
;
325 log_error("Invalid scaleMode %d", scaleMode
);
333 movie_root::StageAlign align
= _stage
->getStageAlignment();
334 movie_root::StageHorizontalAlign halign
= align
.first
;
335 movie_root::StageVerticalAlign valign
= align
.second
;
337 // Handle horizontal alignment
339 case movie_root::STAGE_H_ALIGN_L
:
341 // _xoffset=0 is fine
345 case movie_root::STAGE_H_ALIGN_R
:
348 float defWidth
= swfwidth
*= _xscale
;
349 float diffWidth
= _width
-defWidth
;
350 _xoffset
= diffWidth
;
354 case movie_root::STAGE_V_ALIGN_C
:
357 float defWidth
= swfwidth
*= _xscale
;
358 float diffWidth
= _width
-defWidth
;
359 _xoffset
= diffWidth
/2.0;
365 log_error("Invalid horizontal align %d", valign
);
370 // Handle vertical alignment
372 case movie_root::STAGE_V_ALIGN_T
:
374 // _yoffset=0 is fine
378 case movie_root::STAGE_V_ALIGN_B
:
380 float defHeight
= swfheight
*= _yscale
;
381 float diffHeight
= _height
-defHeight
;
382 _yoffset
= diffHeight
;
386 case movie_root::STAGE_V_ALIGN_C
:
388 float defHeight
= swfheight
*= _yscale
;
389 float diffHeight
= _height
-defHeight
;
390 _yoffset
= diffHeight
/2.0;
396 log_error("Invalid vertical align %d", valign
);
401 //log_debug("updateStageMatrix: scaleMode:%d, valign:%d, halign:%d",
402 //scaleMode, valign, halign);
404 // TODO: have a generic set_matrix ?
405 if (_renderer
.get()) {
406 _renderer
->set_scale(_xscale
, _yscale
);
407 _renderer
->set_translation(_xoffset
, _yoffset
);
409 //log_debug("updateStageMatrix: could not signal updated stage
410 //matrix to renderer (no renderer registered)");
414 //_redraw_flag |= (_width!=width) || (_height!=height);
415 _redraw_flag
= true; // this fixes bug #21971
420 Gui::resize_view(int width
, int height
)
426 if (_stage
&& _started
) {
427 _stage
->setDimensions(width
, height
);
432 _validbounds
.setTo(0, 0, _width
, _height
);
436 if ( _stage
&& _started
) display(_stage
);
444 // @todo since we registered the sound handler, shouldn't we know
445 // already what it is ?!
446 sound::sound_handler
* s
= _stage
->runResources().soundHandler();
450 if (s
->is_muted()) s
->unmute();
456 Gui::notifyMouseMove(int ux
, int uy
)
458 movie_root
* m
= _stage
;
460 if ( ! _started
) return;
462 if ( _stopped
) return;
464 // A stage pseudopixel is user pixel / _xscale wide
465 boost::int32_t x
= (ux
-_xoffset
) / _xscale
;
467 // A stage pseudopixel is user pixel / _xscale high
468 boost::int32_t y
= (uy
-_yoffset
) / _yscale
;
470 #ifdef DEBUG_MOUSE_COORDINATES
471 log_debug(_("mouse @ %d,%d"), x
, y
);
474 if ( m
->mouseMoved(x
, y
) )
476 // any action triggered by the
477 // event required screen refresh
481 DisplayObject
* activeEntity
= m
->getActiveEntityUnderPointer();
484 if ( activeEntity
->isSelectableTextField() )
486 setCursor(CURSOR_INPUT
);
488 else if ( activeEntity
->allowHandCursor() )
490 setCursor(CURSOR_HAND
);
494 setCursor(CURSOR_NORMAL
);
499 setCursor(CURSOR_NORMAL
);
502 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
511 Gui::notifyMouseWheel(int delta
)
513 movie_root
* m
= _stage
;
516 if (!_started
) return;
517 if (_stopped
) return;
519 if (m
->mouseWheel(delta
)) {
520 // any action triggered by the
521 // event required screen refresh
527 Gui::notifyMouseClick(bool mouse_pressed
)
529 movie_root
* m
= _stage
;
532 if (!_started
) return;
533 if (_stopped
) return;
535 if (m
->mouseClick(mouse_pressed
)) {
536 // any action triggered by the
537 // event required screen refresh
545 movie_root
* m
= _stage
;
547 if ( ! _started
) return;
556 Gui::notify_key_event(gnash::key::code k
, int modifier
, bool pressed
)
559 /* Handle GUI shortcuts */
561 if (k
== gnash::key::ESCAPE
) {
562 if (isFullscreen()) {
563 _stage
->setStageDisplayState(movie_root::DISPLAYSTATE_NORMAL
);
567 if (modifier
& gnash::key::GNASH_MOD_CONTROL
) {
597 showUpdatedRegions(!showUpdatedRegions());
599 case gnash::key::MINUS
:
601 // Max interval allowed: 1 second (1FPS)
602 const size_t ni
= std::min
<size_t>(_interval
+ 2, 1000u);
606 case gnash::key::PLUS
:
608 // Min interval allowed: 1/100 second (100FPS)
609 const size_t ni
= std::max
<size_t>(_interval
- 2, 10u);
613 case gnash::key::EQUALS
:
616 const float fps
= _stage
->frameRate();
617 // Min interval allowed: 1/100 second (100FPS)
618 const size_t ni
= 1000.0/fps
;
627 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
628 if ( _keyboardMouseMovements
) {
629 int step
= _keyboardMouseMovementsStep
;
630 // x5 if SHIFT is pressed
631 if (modifier
& gnash::key::GNASH_MOD_SHIFT
) step
*= 5;
635 int newx
= _xpointer
;
636 int newy
= _ypointer
-step
;
637 if ( newy
< 0 ) newy
=0;
638 notifyMouseMove(newx
, newy
);
641 case gnash::key::DOWN
:
643 int newx
= _xpointer
;
644 int newy
= _ypointer
+step
;
645 if ( newy
>= _height
) newy
= _height
-1;
646 notifyMouseMove(newx
, newy
);
649 case gnash::key::LEFT
:
651 int newx
= _xpointer
-step
;
652 int newy
= _ypointer
;
653 if ( newx
< 0 ) newx
= 0;
654 notifyMouseMove(newx
, newy
);
657 case gnash::key::RIGHT
:
659 const int newy
= _ypointer
;
660 int newx
= _xpointer
+ step
;
661 if ( newx
>= _width
) newx
= _width
-1;
662 notifyMouseMove(newx
, newy
);
669 #endif // ENABLE_KEYBOARD_MOUSE_MOVEMENTS
673 if (!_started
) return;
675 if (_stopped
) return;
677 if (_stage
->keyEvent(k
, pressed
)) {
678 // any action triggered by the
679 // event required screen refresh
686 Gui::display(movie_root
* m
)
688 assert(m
== _stage
); // why taking this arg ??
692 InvalidatedRanges changed_ranges
;
695 // Should the frame be rendered completely, even if it did not change?
699 redraw_flag
= _redraw_flag
|| want_redraw();
702 // reset class member if we do a redraw now
703 if (redraw_flag
) _redraw_flag
=false;
705 // Find out the surrounding frame of all characters which
706 // have been updated. This just checks what region of the stage has changed
707 // due to ActionScript code, the timeline or user events. The GUI can still
708 // choose to render a different part of the stage.
712 // choose snapping ranges factor
713 changed_ranges
.setSnapFactor(1.3f
);
715 // Use multi ranges only when GUI/Renderer supports it
716 // (Useless CPU overhead, otherwise)
717 changed_ranges
.setSingleMode(!want_multiple_regions());
719 // scan through all sprites to compute invalidated bounds
720 m
->add_invalidated_bounds(changed_ranges
, false);
722 // grow ranges by a 2 pixels to avoid anti-aliasing issues
723 changed_ranges
.growBy(40.0f
/ _xscale
);
726 changed_ranges
.combineRanges();
730 // TODO: Remove this and want_redraw to avoid confusion!?
732 changed_ranges
.setWorld();
736 // This is a good place to inspect the invalidated bounds state. Enable
737 // the following block (and parts of it) if you need to.
740 // This may print a huge amount of information, but is useful to analyze
741 // the (visible) object structure of the movie and the flags of the
742 // characters. For example, a characters should have set the
743 // m_child_invalidated flag if at least one of it's childs has the
744 // invalidated flag set.
745 log_debug("DUMPING CHARACTER TREE");
748 InfoTree::iterator top
= tr
.begin();
749 _stage
->getMovieInfo(tr
, top
);
751 for (InfoTree::iterator i
= tr
.begin(), e
= tr
.end();
753 std::cout
<< std::string(tr
.depth(i
) * 2, ' ') << i
->first
<< ": " <<
754 i
->second
<< std::endl
;
758 // less verbose, and often necessary: see the exact coordinates of the
759 // invalidated bounds (mainly to see if it's NULL or something else).
760 std::cout
<< "Calculated changed ranges: " << changed_ranges
<< "\n";
764 // Avoid drawing of stopped movies
765 if ( ! changed_ranges
.isNull() ) // use 'else'?
767 // Tell the GUI(!) that we only need to update this
768 // region. Note the GUI can do whatever it wants with
769 // this information. It may simply ignore the bounds
770 // (which will normally lead into a complete redraw),
771 // or it may extend or shrink the bounds as it likes. So,
772 // by calling set_invalidated_bounds we have no guarantee
773 // that only this part of the stage is rendered again.
774 #ifdef REGION_UPDATES_DEBUGGING_FULL_REDRAW
775 // redraw the full screen so that only the
776 // *new* invalidated region is visible
778 InvalidatedRanges world_ranges
;
779 world_ranges
.setWorld();
780 setInvalidatedRegions(world_ranges
);
782 setInvalidatedRegions(changed_ranges
);
785 // TODO: should this be called even if we're late ?
788 // Render the frame, if not late.
789 // It's up to the GUI/renderer combination
790 // to do any clipping, if desired.
793 // show invalidated region using a red rectangle
794 // (Flash debug style)
795 IF_DEBUG_REGION_UPDATES (
796 if (_renderer
.get() && !changed_ranges
.isWorld())
799 for (size_t rno
= 0; rno
< changed_ranges
.size(); rno
++) {
801 const geometry::Range2d
<int>& bounds
=
802 changed_ranges
.getRange(rno
);
805 float xmin
= bounds
.getMinX();
806 float xmax
= bounds
.getMaxX();
807 float ymin
= bounds
.getMinY();
808 float ymax
= bounds
.getMaxY();
818 SWFMatrix no_transform
;
819 _renderer
->draw_poly(corners
, 4,
820 rgba(0,0,0,0), rgba(255,0,0,255), no_transform
, false);
826 // show frame on screen
837 if ( ! _stopped
) return;
840 if ( ! _started
) start();
844 // @todo since we registered the sound handler, shouldn't we know
845 // already what it is ?!
846 sound::sound_handler
* s
= _stage
->runResources().soundHandler();
847 if ( s
) s
->unpause();
849 log_debug("Starting virtual clock");
850 _virtualClock
.resume();
859 // _stage must be registered before this is called.
862 if ( _stopped
) return;
863 if ( isFullscreen() ) unsetFullscreen();
867 // @todo since we registered the sound handler, shouldn't we know
868 // already what it is ?!
869 sound::sound_handler
* s
= _stage
->runResources().soundHandler();
872 log_debug("Pausing virtual clock");
873 _virtualClock
.pause();
886 // TODO: call stop() instead ?
887 // The only thing I see is that ::stop exits full-screen,
888 // but I'm not sure that's intended behaviour
890 // @todo since we registered the sound handler, shouldn't we know
891 // already what it is ?!
892 sound::sound_handler
* s
= _stage
->runResources().soundHandler();
896 log_debug("Pausing virtual clock");
897 _virtualClock
.pause();
905 assert ( ! _started
);
907 log_debug("Gui is in stop mode, won't start application");
911 // Initializes the stage with a Movie and the passed flash vars and
912 // Scriptable vars for ExternalInterface.
913 _stage
->init(_movieDef
.get(), _flashVars
, _scriptableVars
);
915 bool background
= true; // ??
916 _stage
->set_background_alpha(background
? 1.0f
: 0.05f
);
918 // @todo since we registered the sound handler, shouldn't we know
919 // already what it is ?!
920 sound::sound_handler
* s
= _stage
->runResources().soundHandler();
921 if ( s
) s
->unpause();
924 // to properly update stageMatrix if scaling is given
925 resize_view(_width
, _height
);
927 log_debug("Starting virtual clock");
928 _virtualClock
.resume();
944 gnash::movie_root
* m
= _stage
;
946 // Define REVIEW_ALL_FRAMES to have *all* frames
947 // consequentially displayed. Useful for debugging.
948 //#define REVIEW_ALL_FRAMES 1
950 #ifndef REVIEW_ALL_FRAMES
951 // Advance movie by one frame
952 const bool advanced
= m
->advance();
954 const size_t cur_frame
= m
->getRootMovie()->get_current_frame();
955 const size_t tot_frames
= m
->getRootMovie()->get_frame_count();
956 const bool advanced
= m
->advance();
958 m
->getRootMovie
.ensureFrameLoaded(tot_frames
);
959 m
->goto_frame(cur_frame
+ 1);
960 m
->set_play_state(gnash::MovieClip::PLAYSTATE_PLAY
);
961 log_debug(_("Frame %d"), m
->get_current_frame());
964 #ifdef GNASH_FPS_DEBUG
965 // will be a no-op if fps_timer_interval is zero
972 // TODO: ask stage about doDisplay ?
973 // - if it didn't advance might need to check updateAfterEvent
974 bool doDisplay
= true;
976 #ifdef SKIP_RENDERING_IF_LATE
977 // We want to skip rendering IFF it's time to advance again.
978 // We'll ask the stage about it
979 if (_stage
->timeToNextFrame() <= 0) {
981 // or should it be if advanced ?
983 // TODO: take note of a frame drop (count them)
984 //log_debug("Frame rendering dropped due to being late");
985 #ifdef GNASH_FPS_DEBUG
991 #endif // ndef SKIP_RENDERING_IF_LATE
993 if (doDisplay
) display(m
);
996 size_t curframe
= m
->get_current_frame(); // can be 0 on malformed SWF
997 const gnash::MovieClip
& si
= m
->getRootMovie();
998 if (curframe
+ 1 >= si
.get_frame_count()) {
1003 if (_screenShotter
.get()) {
1004 _screenShotter
->screenShot(_advances
);
1007 // Only increment advances and check for exit condition when we've
1008 // really changed frame.
1010 /// Quit if we've reached the frame advance limit.
1011 if (_maxAdvances
&& (_advances
> _maxAdvances
)) {
1021 Gui::takeScreenShot()
1023 if (!_screenShotter
.get()) {
1024 // If no ScreenShotter exists, none was requested at startup.
1025 // We use a default filename pattern.
1026 URL
url(_runResources
.streamProvider().originalURL());
1027 std::string::size_type p
= url
.path().rfind('/');
1028 const std::string
& name
= (p
== std::string::npos
) ? url
.path() :
1029 url
.path().substr(p
+ 1);
1030 const std::string
& filename
= "screenshot-" + name
+ "-%f";
1031 _screenShotter
.reset(new ScreenShotter(_renderer
, filename
));
1033 assert (_screenShotter
.get());
1034 _screenShotter
->now();
1038 Gui::requestScreenShots(const ScreenShotter::FrameList
& l
, bool last
,
1039 const std::string
& filename
)
1041 // Nothing to do if there is no renderer or if no frames should be
1043 if (!_renderer
.get() || (l
.empty() && !last
)) {
1047 _screenShotter
.reset(new ScreenShotter(_renderer
, filename
));
1048 if (last
) _screenShotter
->lastFrame();
1049 _screenShotter
->setFrames(l
);
1054 Gui::setCursor(gnash_cursor_type
/*newcursor*/)
1066 Gui::setInvalidatedRegion(const SWFRect
& /*bounds*/)
1072 Gui::setInvalidatedRegions(const InvalidatedRanges
& ranges
)
1074 // fallback to single regions
1075 geometry::Range2d
<int> full
= ranges
.getFullArea();
1079 if (full
.isFinite()) {
1080 bounds
= SWFRect(full
.getMinX(), full
.getMinY(),
1081 full
.getMaxX(), full
.getMaxY());
1083 else if (full
.isWorld()) {
1087 setInvalidatedRegion(bounds
);
1092 std::auto_ptr
<movie_root::InfoTree
>
1093 Gui::getMovieInfo() const
1095 std::auto_ptr
<movie_root::InfoTree
> tr
;
1101 tr
.reset(new movie_root::InfoTree());
1103 // Top nodes for the tree:
1104 // 1. VM information
1105 // 2. "Stage" information
1108 movie_root::InfoTree::iterator topIter
= tr
->begin();
1109 movie_root::InfoTree::iterator firstLevelIter
;
1111 VM
& vm
= _stage
->getVM();
1113 std::ostringstream os
;
1118 os
<< "SWF " << vm
.getSWFVersion();
1119 topIter
= tr
->insert(topIter
, std::make_pair("VM version", os
.str()));
1121 // This short-cut is to avoid a bug in movie_root's getMovieInfo,
1122 // which relies on the availability of a _rootMovie for doing
1123 // it's work, while we don't set it if we didn't start..
1126 topIter
= tr
->insert(topIter
, std::make_pair("Stage properties",
1127 "not constructed yet"));
1131 movie_root
& stage
= vm
.getRoot();
1132 stage
.getMovieInfo(*tr
, topIter
);
1137 topIter
= tr
->insert(topIter
, std::make_pair("Mouse Entities", ""));
1139 const DisplayObject
* ch
;
1140 ch
= stage
.getActiveEntityUnderPointer();
1142 std::stringstream ss
;
1143 ss
<< ch
->getTarget() << " (" + typeName(*ch
)
1144 << " - depth:" << ch
->get_depth()
1145 << " - useHandCursor:" << ch
->allowHandCursor()
1147 firstLevelIter
= tr
->append_child(topIter
,
1148 std::make_pair("Active entity under mouse pointer", ss
.str()));
1151 ch
= stage
.getEntityUnderPointer();
1153 std::stringstream ss
;
1154 ss
<< ch
->getTarget() << " (" + typeName(*ch
)
1155 << " - depth:" << ch
->get_depth()
1157 firstLevelIter
= tr
->append_child(topIter
,
1158 std::make_pair("Topmost entity under mouse pointer", ss
.str()));
1161 ch
= stage
.getDraggingCharacter();
1163 std::stringstream ss
;
1164 ss
<< ch
->getTarget() << " (" + typeName(*ch
)
1165 << " - depth:" << ch
->get_depth() << ")";
1166 firstLevelIter
= tr
->append_child(topIter
,
1167 std::make_pair("Dragging character: ", ss
.str()));
1173 topIter
= tr
->insert(topIter
, std::make_pair("GC Statistics", ""));
1174 GC::CollectablesCount cc
;
1175 _stage
->gc().countCollectables(cc
);
1177 const std::string lbl
= "GC managed ";
1178 for (GC::CollectablesCount::iterator i
=cc
.begin(), e
=cc
.end(); i
!=e
; ++i
) {
1179 const std::string
& typ
= i
->first
;
1180 std::ostringstream ss
;
1182 firstLevelIter
= tr
->append_child(topIter
,
1183 std::make_pair(lbl
+ typ
, ss
.str()));
1186 tr
->sort(firstLevelIter
.begin(), firstLevelIter
.end());
1193 #ifdef GNASH_FPS_DEBUG
1195 Gui::fpsCounterTick()
1198 // increment this *before* the early return so that
1199 // frame count on exit is still valid
1200 ++fps_counter_total
;
1202 if (! fps_timer_interval
) {
1206 boost::uint64_t current_timer
= clocktime::getTicks();
1208 // TODO: keep fps_timer_interval in milliseconds to avoid the multiplication
1209 // at each fpsCounterTick call...
1210 boost::uint64_t interval_ms
= (boost::uint64_t)(fps_timer_interval
* 1000.0);
1212 if (fps_counter_total
==1) {
1213 fps_timer
= current_timer
;
1214 fps_start_timer
= current_timer
;
1219 if (current_timer
- fps_timer
>= interval_ms
) {
1221 float secs
= (current_timer
- fps_timer
) / 1000.0;
1222 float secs_total
= (current_timer
- fps_start_timer
)/1000.0;
1224 float rate
= fps_counter
/secs
;
1226 if (secs
> 10000000) {
1227 // the timers are unsigned, so when the clock runs "backwards" it leads
1228 // to a very high difference value. In theory, this should never happen
1229 // with ticks, but it does on my machine (which may have a hw problem?).
1230 std::cerr
<< "Time glitch detected, need to restart FPS counters, sorry..." << std::endl
;
1232 fps_timer
= current_timer
;
1233 fps_start_timer
= current_timer
;
1234 fps_counter_total
= 0;
1239 // first FPS message?
1240 if (fps_timer
== fps_start_timer
) { // they're ints, so we can compare
1241 fps_rate_min
= rate
;
1242 fps_rate_max
= rate
;
1244 fps_rate_min
= std::min
<float>(fps_rate_min
, rate
);
1245 fps_rate_max
= std::max
<float>(fps_rate_max
, rate
);
1248 float avg
= fps_counter_total
/ secs_total
;
1250 //log_debug("Effective frame rate: %0.2f fps", (float)(fps_counter/secs));
1251 std::cerr
<< boost::format("Effective frame rate: %0.2f fps "
1252 "(min %0.2f, avg %0.2f, max %0.2f, "
1253 "%u frames in %0.1f secs total, "
1254 "dropped %u)") % rate
%
1255 fps_rate_min
% avg
% fps_rate_max
%
1256 fps_counter_total
% secs_total
%
1257 frames_dropped
<< std::endl
;
1260 fps_timer
= current_timer
;
1268 Gui::addFlashVars(Gui::VariableMap
& from
)
1270 for (VariableMap::iterator i
=from
.begin(), ie
=from
.end(); i
!=ie
; ++i
) {
1271 _flashVars
[i
->first
] = i
->second
;
1276 Gui::addScriptableVar(const std::string
&name
, const std::string
&value
)
1278 log_debug("Adding scriptable variable \"%s\" = %s",
1280 _scriptableVars
[name
] = value
;
1284 Gui::setMovieDefinition(movie_definition
* md
)
1291 Gui::setStage(movie_root
* stage
)
1299 Gui::yesno(const std::string
& question
)
1301 log_error("This gui didn't override 'yesno', assuming 'yes' answer to "
1302 "question: %s", question
);
1307 Gui::setQuality(Quality q
)
1310 log_error("Gui::setQuality called before a movie_root was available");
1313 _stage
->setQuality(q
);
1317 Gui::getQuality() const
1320 log_error("Gui::getQuality called before a movie_root was available");
1322 return QUALITY_HIGH
;
1324 return _stage
->getQuality();
1328 Gui::setFDCallback(int fd
, boost::function
<void ()> callback
)
1330 log_debug("Setting callback for fd #%d", fd
);
1332 _fd_callbacks
[fd
] = callback
;
1339 Gui::callCallback(int fd
)
1341 std::map
<int, boost::function
<void ()> >::iterator it
= _fd_callbacks
.find(fd
);
1343 if (it
== _fd_callbacks
.end()) {
1344 log_error("Attempted to call a callback for an unregistered fd.");
1348 boost::function
<void()>& f
= it
->second
;
1354 ScreenShotter::saveImage(const std::string
& id
) const
1356 // Replace all "%f" in the filename with the frameAdvance.
1357 std::string
outfile(_fileName
);
1358 boost::replace_all(outfile
, "%f", id
);
1360 FILE* f
= std::fopen(outfile
.c_str(), "wb");
1362 boost::shared_ptr
<IOChannel
> t(new tu_file(f
, true));
1363 _renderer
->renderToImage(t
, GNASH_FILETYPE_PNG
);
1365 log_error("Failed to open screenshot file \"%s\"!", outfile
);
1370 ScreenShotter::screenShot(size_t frameAdvance
)
1372 // Save an image if an spontaneous screenshot was requested or the
1373 // frame is in the list of requested frames.
1374 if (_immediate
|| std::binary_search(_frames
.begin(), _frames
.end(),
1376 saveImage(boost::lexical_cast
<std::string
>(frameAdvance
));
1382 ScreenShotter::last() const
1390 ScreenShotter::setFrames(const FrameList
& frames
)
1393 std::sort(_frames
.begin(), _frames
.end());
1401 // indent-tabs-mode: nil