1 //
2 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
3 //   Free Software 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.
14 //
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
18 
19 
20 /// \page events_handling Handling of user events
21 ///
22 /// There are two kinds of events:
23 /// - system generated
24 /// - user generated
25 ///
26 /// System generated events are those like load, data recive, unload,
27 /// enter frame, etc.
28 /// User generated events are mouse movements and clicks, keyboard activity.
29 ///
30 /// Events can trigger actions execution, if "handlers" are specified for
31 /// a specific event with ActionScript code.
32 /// The actions triggered by user events are executed *immediately*, not
33 /// at the next frame iteration. Nonetheless, since rendering of the stage
34 /// usually happens at fixed rate (frame rate) you won't see the effects
35 /// of actions execution until next iteration... unless...
36 ///
37 /// Well, *some* events actions always trigger immediate redisplay, while
38 /// some others require a call to a special function to do so.
39 ///
40 /// The events actions that trigger immediate redisplay are Button actions.
41 /// Colin Mook, in his "ActionScript - The Definitive Guide" sais:
42 /// << Buttons naturally update between frames >>
43 ///
44 /// Other events, in particular MovieClip events such as mouseDown, mouseUp,
45 /// mouseMove, keyDown and keyUp don't by default trigger redisplay, unless
46 /// the attached action code makes a call to the special function named
47 /// 'updateAfterEvent()'.
48 ///
49 /// For this purpose, user events notification functions in gnash core
50 /// library return a boolean value, which tells wheter any action triggered
51 /// by the event requires immediate redisplay.
52 ///
53 /// At the time of writing (2006-10-19) this is not implemented yet and
54 /// the return code is always TRUE. We shall work on it :)
55 ///
56 /// The events notification functions that currently support this interface
57 /// are:
58 ///
59 /// - bool movie_root::notify_mouse_moved(int x, int y);
60 /// - bool movie_root::notify_mouse_clicked(bool mouse_pressed, int mask);
61 /// - bool keyEvent(key::code k, bool down);
62 
63 
64 #ifndef GNASH_MOVIE_ROOT_H
65 #define GNASH_MOVIE_ROOT_H
66 
67 #ifdef HAVE_CONFIG_H
68 #include "gnashconfig.h" //USE_SWFTREE
69 #endif
70 
71 #include <map>
72 #include <string>
73 #include <vector>
74 #include <forward_list>
75 #include <set>
76 #include <bitset>
77 #include <array>
78 #include <boost/ptr_container/ptr_deque.hpp>
79 #include <boost/noncopyable.hpp>
80 #include <boost/any.hpp>
81 #include <boost/optional.hpp>
82 
83 #include "dsodefs.h" // DSOEXPORT
84 #include "DragState.h"
85 #include "MouseButtonState.h" // for composition
86 #include "GnashKey.h" // key::code
87 #include "GnashEnums.h"
88 #include "MovieClip.h"
89 #include "SimpleBuffer.h" // for LoadCallback
90 #include "MovieLoader.h"
91 #include "ExternalInterface.h"
92 #include "GC.h"
93 #include "VM.h"
94 #include "HostInterface.h"
95 #include "log.h"
96 #include "IOChannel.h"
97 
98 #ifdef USE_SWFTREE
99 # include "tree.hh"
100 #endif
101 
102 // GNASH_PARANOIA_LEVEL:
103 // 0 : (not unimplemented)
104 // 1 : quick assertions
105 // 2 : add testInvariant
106 //
107 #ifndef GNASH_PARANOIA_LEVEL
108 # define GNASH_PARANOIA_LEVEL 1
109 #endif
110 
111 // Forward declarations
112 namespace gnash {
113     class ExecutableCode;
114     class URL;
115     class Timer;
116     class MovieClip;
117     class VirtualClock;
118     class RunResources;
119     class Button;
120     class VM;
121     class Movie;
122 }
123 
124 namespace gnash {
125 
126 struct DepthComparator
127 {
128     typedef MovieClip* LevelMovie;
operatorDepthComparator129     bool operator()(const LevelMovie& d1, const LevelMovie& d2) const {
130         return d1->get_depth() < d2->get_depth();
131     }
132 };
133 
134 /// This class represents the 'Stage' and top-level movie.
135 //
136 /// It is a wrapper around the set of loaded levels being played. Each
137 /// 'run' of a SWF movie, including all further movies loaded during the
138 /// run, has exactly one movie_root, which is kept for the entire run.
139 /// Loading a new top-level movie does not create a new movie_root.
140 //
141 /// The 'Stage' part of movie_root is accessible through the ActionScript
142 /// Stage object, implemented in Stage_as.cpp.
143 //
144 /// The movie_root class is responsible for accepting and passing on
145 /// user events (mouse or key events), for maintaining the heart-beat
146 /// mechanism, and for advancing all MovieClips on request from the
147 /// hosting application.
148 //
149 /// The _root object is provided by getAsRoot().
150 class DSOEXPORT movie_root : public GcRoot, boost::noncopyable
151 {
152 public:
153 
154     class LoadCallback {
155     public:
LoadCallback(std::unique_ptr<IOChannel> s,as_object * o)156         LoadCallback(std::unique_ptr<IOChannel> s, as_object* o)
157             :
158             _stream(std::move(s)),
159             _obj(o)
160         {}
161         bool processLoad();
162         void setReachable() const;
163     private:
164         std::unique_ptr<IOChannel> _stream;
165         SimpleBuffer _buf;
166         as_object* _obj;
167     };
168     typedef std::list<LoadCallback> LoadCallbacks;
169 
170     typedef std::bitset<key::KEYCOUNT> Keys;
171 
172     /// Default constructor
173     //
174     /// Make sure to call setRootMovie()
175     /// before using any of this class methods !
176     movie_root(VirtualClock& clock, const RunResources& runResources);
177 
178     ~movie_root();
179 
180     /// Initialize movie_root with a parsed movie definition
181     //
182     /// The definition may be a SWF or Bitmap movie definition.
183     //
184     /// The created Movie is returned; it is non-const so may be stored,
185     /// queried, and changed by the caller for debugging or manipulation.
186     /// Direct use of the pointer may result in unexpected behaviour during
187     /// SWF playback, so for normal playback this pointer should not be
188     /// used.
189     Movie* init(movie_definition* def,
190             const MovieClip::MovieVariables& variables);
191 
192     /// Return the movie at the given level (0 if unloaded level).
193     //
194     /// POST CONDITIONS:
195     /// - The returned DisplayObject has a depth equal to 'num'
196     ///
197     MovieClip* getLevel(unsigned int num) const;
198 
199     /// Put the given movie at the given level
200     //
201     /// @param movie
202     /// The Movie to store at the given level.
203     /// Its depth will be set to <num>+DisplayObject::staticDepthOffset and
204     /// its name to _level<num>
205     void setLevel(unsigned int num, Movie* movie);
206 
207     /// Replace an existing level with a new movie
208     //
209     /// Depth will be assigned to external_movie by this function.
210     /// If the give level number doesn't exist an error is logged
211     /// and nothing else happens.
212     ///
213     /// This method is intended for use by xxx.loadMovie(yyy)
214     /// when 'xxx' is a top-level movie.
215     ///
216     void replaceLevel(unsigned int num, Movie* external_movie);
217 
218     /// Swap depth of a level (or two)
219     //
220     /// Character's depths are updated.
221     ///
222     /// @param sp
223     ///    The level to change depth/level of. A pointer to it is expected
224     ///    to be found in the _level# container, or an error will be printed
225     ///    and the call would result in a no-op.
226     ///
227     /// @param depth
228     ///    New depth to assign to the DisplayObject. If another level
229     ///    exists at the target depth the latter is moved in place of
230     ///    the former, with its depth also updated.
231     ///
232     void swapLevels(MovieClip* sp, int depth);
233 
234     /// Drop level at given depth.
235     //
236     /// @param depth
237     ///   Depth of the level to drop. Note that this is
238     ///   -DisplayObject::staticDepthOffset for the root movie. Must be >=0 and
239     ///   <= 1048575 or an assertion will fail. Note that if the depth
240     ///   evaluates to the original root movie nothing happens (not allowed
241     ///   to remove that). It is not tested if it's allowed to remove _level0
242     ///   after loading into it.
243     void dropLevel(int depth);
244 
245     /// Change stage size
246     //
247     /// This may be smaller than the size of the root movie. It determines
248     /// how much of the movie is visible.
249     //
250     /// @param w    The width of the stage
251     /// @param h    The height of the stage.
252     void setDimensions(size_t w, size_t h);
253 
254     /// Notional width of the stage, actual value depending on scaleMode
255     size_t getStageWidth() const;
256 
257     /// Notional height of the stage, actual value depending on scaleMode
258     size_t getStageHeight() const;
259 
260     /// Inform the Stage that the mouse has moved.
261     //
262     /// Coordinates are in Stage Coordinate Space (pseudo-pixels units).
263     ///
264     /// @param x    The x co-ordinate in pixels.
265     /// @param y    The y co-ordinate in pixels.
266     /// @return     true if any action triggered requires a redraw.
267     ///
268     /// TODO: take twips (or float pixels), or we won't be able to
269     ///       support sub-pixel accuracy in collision detection.
270     DSOEXPORT bool mouseMoved(std::int32_t x, std::int32_t y);
271 
272     /// Inform the Stage that a mouse click has occurred.
273     //
274     /// @param press    true for a mouse click, false for a release
275     /// @return         true if any action triggered requires a redraw.
276     DSOEXPORT bool mouseClick(bool press);
277 
278     /// Inform the Stage that a mouse wheel has moved.
279     //
280     /// @param delta    The direction of the scroll: positive for up, negative
281     ///                 for down. Although values from about -3 to 3 are
282     ///                 documented, only -1 and 1 have been observed.
283     /// @return         true if any action triggered requires a redraw.
284     DSOEXPORT bool mouseWheel(int delta);
285 
286     /// Tell the movie when the user pressed or released a key.
287     //
288     /// This function should return TRUE if any action triggered
289     /// by the event requires redraw, see \ref events_handling for
290     /// more info.
291     DSOEXPORT bool keyEvent(key::code k, bool down);
292 
293     /// Use this to retrieve the last state of the mouse.
294     //
295     /// Coordinates are in PIXELS, NOT TWIPS.
296     std::pair<std::int32_t, std::int32_t> mousePosition() const;
297 
298     void setDragState(const DragState& st);
299 
300     /// Access the originating root movie (not necessarily _level0)
301     //
302     /// @return the original root movie.
getRootMovie()303     Movie& getRootMovie() {
304         return *_rootMovie;
305     }
306 
stop_drag()307     void stop_drag() {
308         _dragState.reset();
309     }
310 
311     /// Add an interval timer
312     //
313     /// @param timer
314     /// A Timer, ownership will be transferred. Must not be NULL.
315     ///
316     /// @param internal
317     /// If true, this is an internal timer, so will get a negative id.
318     ///
319     /// @return An integer indentifying the timer
320     ///         for subsequent call to clear_interval_timer.
321     ///         It will NEVER be zero.
322     std::uint32_t addIntervalTimer(std::unique_ptr<Timer> timer);
323 
324     /// Register an object for loading data to.
325     //
326     /// When complete, the object's onData function is called.
327     /// The callback is removed when the load is complete, including failed
328     /// loads.
329     //
330     /// There is no restriction on the type of as_object that can registered.
331     //
332     /// @param obj      The object to update when data is received.
333     /// @param str      The stream to load from.
334     //
335     /// TODO: this function could be improved, e.g. by handling the
336     /// URL checking and stream construction as well.
337     //
338     /// It may be possible for this function to handle all connections if
339     /// it also takes a callback function to call on each advance.
340     void addLoadableObject(as_object* obj, std::unique_ptr<IOChannel> str);
341 
342     void addAdvanceCallback(ActiveRelay* obj);
343 
344     void removeAdvanceCallback(ActiveRelay* obj);
345 
346     /// Remove timer identified by given integer
347     //
348     /// @return true on success, false on error (no such timer)
349     bool clearIntervalTimer(std::uint32_t x);
350 
351     void set_background_color(const rgba& color);
352 
353     void set_background_alpha(float alpha);
354 
355     /// Return the VM used by this movie_root
getVM()356     VM& getVM() { return _vm; }
357 
358     /// Main and only callback from hosting application.
359     /// Expected to be called at 10ms resolution.
360     //
361     /// @return true if the heart-beat resulted in actual
362     ///         SWF playhead advancement (frame advancement)
363     ///
364     bool advance();
365 
366     /// \brief
367     /// Return the number of milliseconds available before
368     /// it's time to advance the timeline again.
369     //
370     /// Return value can be negative if we're late...
371     ///
372     int timeToNextFrame() const;
373 
374     /// Entry point for movie advancement
375     //
376     /// This function does:
377     ///   - Execute all timers
378     ///   - Reset the next Random number
379     ///   - Advance all advanceable DisplayObjects in reverse-placement order
380     ///   - Cleanup key listeners
381     ///   - Process all queued actions
382     ///   - Remove unloaded DisplayObjects from the advanceable
383     ///     DisplayObjects list.
384     ///   - Run the GC collector
385     void advanceMovie();
386 
387     void display();
388 
389     /// Get a unique number for unnamed instances.
nextUnnamedInstance()390     size_t nextUnnamedInstance() {
391         return ++_unnamedInstance;
392     }
393 
394     /// Push a new DisplayObject listener for key events
395     void registerButton(Button* listener);
396 
397     /// Remove a DisplayObject listener for key events
398     void removeButton(Button* listener);
399 
400     /// Get the DisplayObject having focus
401     //
402     /// The DisplayObject having focus will receive mouse button
403     /// and key presses/releases.
404     ///
405     /// @return the DisplayObject having focus or NULL of none.
406     ///
407     DisplayObject* getFocus();
408 
409     /// Set the DisplayObject having focus
410     //
411     /// @param to
412     /// The DisplayObject to receive focus. NULL to kill focus.
413     /// @return true if the focus operation succeeded, false if the passed
414     /// DisplayObject cannot receive focus. setFocus(0) is a valid operation, so
415     /// returns true (always succeeds).
416     bool setFocus(DisplayObject* to);
417 
418     DSOEXPORT void add_invalidated_bounds(InvalidatedRanges& ranges,
419             bool force);
420 
421     /// Return the topmost active entity under the pointer
422     //
423     /// This method returns cached info, with cache updated
424     /// by notify_mouse_moved (and should be updated also
425     /// by movie advancement or actions execution maybe, not
426     /// currently implmented).
427     ///
428     /// @return the topmost active entity under pointer or NULL if none.
429     DisplayObject* getActiveEntityUnderPointer() const;
430 
431     /// Return the topmost non-dragging entity under the pointer
432     //
433     /// This method triggers a displaylist scan
434     ///
435     /// @return the topmost non-dragging entity under pointer or NULL if none
436     const DisplayObject* getEntityUnderPointer() const;
437 
438     /// Return the DisplayObject currently being dragged, if any
439     DisplayObject* getDraggingCharacter() const;
440 
441     bool testInvariant() const;
442 
443     /// The possible values of Stage.displayState
444     enum DisplayState {
445         DISPLAYSTATE_NORMAL,
446         DISPLAYSTATE_FULLSCREEN
447     };
448 
449     /// The possibile values of Stage.scaleMode
450     enum ScaleMode {
451         SCALEMODE_SHOWALL,
452         SCALEMODE_NOSCALE,
453         SCALEMODE_EXACTFIT,
454         SCALEMODE_NOBORDER
455     };
456 
457     /// The possible horizonal positions of the Stage
458     enum StageHorizontalAlign {
459         STAGE_H_ALIGN_C,
460         STAGE_H_ALIGN_L,
461         STAGE_H_ALIGN_R
462     };
463 
464     /// The possible vertical position of the Stage
465     enum StageVerticalAlign {
466         STAGE_V_ALIGN_C,
467         STAGE_V_ALIGN_T,
468         STAGE_V_ALIGN_B
469     };
470 
471     /// The possible elements of a Stage.alignMode.
472     enum AlignMode {
473         STAGE_ALIGN_L,
474         STAGE_ALIGN_T,
475         STAGE_ALIGN_R,
476         STAGE_ALIGN_B
477     };
478 
479     /// The possibile values of AllowScriptAccess
480     enum AllowScriptAccessMode {
481         SCRIPT_ACCESS_NEVER,
482         SCRIPT_ACCESS_SAME_DOMAIN,
483         SCRIPT_ACCESS_ALWAYS
484     };
485 
486     /// Set the current display quality of the entire SWF.
487     void setQuality(Quality q);
488 
489     /// Get the current display quality.
getQuality()490     Quality getQuality() const { return _quality; }
491 
492     /// Sets movie_root's horizontal and vertical alignment to one
493     /// of the three possible positions for each dimension.
494     void setStageAlignment(short s);
495 
496     /// Sets the flag to allow interfacing with JavaScript in the browser.
497     /// This is disabled by default, but enabled for ExternalInterface.
498     void setAllowScriptAccess(AllowScriptAccessMode mode);
499 
500     /// Gets the current Access Mode for ExternalInterface.
501     AllowScriptAccessMode getAllowScriptAccess();
502 
503     typedef std::pair<StageHorizontalAlign, StageVerticalAlign> StageAlign;
504 
505     /// Returns the current alignment of the stage (left/right/centre, top/
506     /// bottom/centre) as a std::pair
507     StageAlign getStageAlignment() const;
508 
509     /// Returns the current value of _showMenu which instructs the gui about
510     /// how much to display in the context menu
511     bool getShowMenuState() const;
512 
513     /// Sets the value of _showMenu and calls the fscommand handler for the
514     /// current gui
515     void setShowMenuState(bool state);
516 
517     /// Sets the Stage object's align mode.
518     void setStageScaleMode(ScaleMode sm);
519 
520     /// Returns the Stage object's align mode.
getStageScaleMode()521     ScaleMode getStageScaleMode() const { return _scaleMode; }
522 
523     // The string representation of the current align mode.
524     std::string getStageAlignMode() const;
525 
526     /// Returns the Stage object's align mode.
getStageDisplayState()527     DisplayState getStageDisplayState() const { return _displayState; }
528 
529     // The string representation of the current align mode.
530     void setStageDisplayState(const DisplayState ds);
531 
532     /// Action priority levels
533     enum ActionPriorityLevel {
534         /// Init actions, Init event handlers
535         PRIORITY_INIT,
536         /// Construct event handlers
537         PRIORITY_CONSTRUCT,
538         /// Frame actions, load handlers, unload handlers
539         PRIORITY_DOACTION,
540         /// Last element used to easy computation of size...
541         PRIORITY_SIZE
542     };
543 
544     /// A number of queues of code to execute
545     //
546     /// This is a ptr_deque because it needs no insertion in the middle but
547     /// frequent push_back and pop_front. We also have to traverse it, so
548     /// a queue is not usable.
549     typedef std::array<boost::ptr_deque<ExecutableCode>, PRIORITY_SIZE>
550         ActionQueue;
551 
552     /// Push an executable code to the ActionQueue
553     void pushAction(std::unique_ptr<ExecutableCode> code, size_t lvl);
554 
555     /// Push an executable code to the ActionQueue
556     void pushAction(const action_buffer& buf, DisplayObject* target);
557 
558     /// Mark all reachable resources (for GC)
559     //
560     /// Resources reachable from movie_root are:
561     ///
562     /// - All _level# movies (_movies)
563     /// - The original root movie (_rootMovie)
564     /// - Mouse entities (m_mouse_button_state)
565     /// - Timer targets (_intervalTimers)
566     /// - ExternalInterace callbacks (_externalCallbackMethods and
567     ///   _externalCallbackInstances)
568     /// - Resources reachable by ActionQueue code (_actionQueue)
569     /// - Any DisplayObject being dragged
570     void markReachableResources() const;
571 
572     /// \brief
573     /// Register a newly born advanceable DisplayObject to the
574     /// list of DisplayObjects to be advanced on next ::advance call.
575     //
576     /// The DisplayObject will only be advanced if not unloaded when
577     /// its turn comes. Characters are advanced in reverse-placement
578     /// order (first registered is advanced last)
579     ///
addLiveChar(MovieClip * ch)580     void addLiveChar(MovieClip* ch)
581     {
582         // Don't register the object in the list twice
583 #if GNASH_PARANOIA_LEVEL > 1
584         assert(std::find(_liveChars.begin(), _liveChars.end(), ch) ==
585             _liveChars.end());
586 #endif
587         _liveChars.push_front(ch);
588     }
589 
590     /// Reset stage to its initial state
591     void reset();
592 
593     /// Call this method for disabling run of actions
594     //
595     /// NOTE: this will only work for queued actions, not
596     ///       for *every* action. Supposedly all actions should
597     ///       be queued, but this is not really always the case.
598     ///       Notable exceptions are:
599     ///         - Actions in callFrame target frame
600     ///           but only executed by execution of the callFrame opcode
601     ///         - on{,Clip}{Initialize,Construct} event handlers
602     ///         - User event handlers (mouse,keyboard)
603     ///
604     void disableScripts();
605 
606     /// Return true if scripts execution is disabled
scriptsDisabled()607     bool scriptsDisabled() const { return _disableScripts; };
608 
609     /// Process action queues with higher priority then the priority
610     /// of the action queue currently being processed.
611     //
612     /// This is intended to be called at the end of any function call
613     /// and at the end of an action block.
614     ///
615     /// TODO: be aware of infinite loops !
616     ///
617     void flushHigherPriorityActionQueues();
618 
619     DisplayObject* findCharacterByTarget(const std::string& tgtstr) const;
620 
621     /// Queue a request for loading a movie
622     //
623     /// This function constructs the URL and, if required, the postdata
624     /// from the arguments. The variables to send should *not* be appended
625     /// to @param urlstr before calling this function.
626     //
627     /// @param urlstr   The url exactly as requested. This may already
628     ///                 contain a query string.
629     /// @param target   Target for request.
630     /// @param data     The variables data to send, URL encoded in
631     ///                 key/value pairs
632     /// @param method   The VariablesMethod to use for sending the data. If
633     ///                 MovieClip::METHOD_NONE, no data will be sent.
634     /// @param handler  An object which will be signalled of load
635     ///                 events (onLoadStart, onLoadComplete, onLoadInit,
636     ///                 onLoadError). Can be null if caller doesn't care.
637     ///
638     void loadMovie(const std::string& url, const std::string& target,
639             const std::string& data, MovieClip::VariablesMethod method,
640             as_object* handler=nullptr)
641     {
642         _movieLoader.loadMovie(url, target, data, method, handler);
643     }
644 
645     /// Send a request to the hosting application (e.g. browser).
646     //
647     /// This function constructs the URL and, if required, the postdata
648     /// from the arguments. The variables to send should *not* be appended
649     /// to @param urlstr before calling this function.
650     //
651     /// @param urlstr   The url exactly as requested. This may already
652     ///                 contain a query string.
653     /// @param target   Target for request.
654     /// @param data     The variables data to send, URL encoded in
655     ///                 key/value pairs
656     /// @param method   The VariablesMethod to use for sending the data. If
657     ///                 MovieClip::METHOD_NONE, no data will be sent.
658     void getURL(const std::string& urlstr, const std::string& target,
659             const std::string& data, MovieClip::VariablesMethod method);
660 
661 
lastKeyEvent()662     key::code lastKeyEvent() const {
663         return _lastKeyEvent;
664     }
665 
unreleasedKeys()666     const Keys& unreleasedKeys() const {
667         return _unreleasedKeys;
668     }
669 
670     /// Register an actionscript class for construction of a MovieClip
671     //
672     /// @param sprite   The definition tag for the MovieClip to be placed on
673     ///                 stage
674     /// @param class    The ActionScript class to be used in construction.
675     void registerClass(const SWF::DefinitionTag* sprite, as_function* cls);
676 
677     /// Get the actionscript class for constructing a MovieClip
678     //
679     /// @param sprite   The definition tag for the MovieClip to be placed on
680     ///                 stage
681     /// @return         The class to be used, or 0 if no class is associated.
682     as_function* getRegisteredClass(const SWF::DefinitionTag* sprite) const;
683 
684     /// Set a filedescriptor to use for host application requests
685     /// (for browser communication mostly)
setHostFD(int fd)686     void setHostFD(int fd) {
687         assert(fd >= 0);
688         _hostfd = fd;
689     }
690 
691     /// Set a filedescriptor to use for host application requests
692     /// (for browser communication mostly)
setControlFD(int fd)693     void setControlFD(int fd) {
694         _controlfd = fd;
695     }
696 
697     /// Get the filedescriptor to use for host application requests
698     /// (for browser communication mostly)
699     ///
700     /// @return -1 if no filedescriptor is provided by host app.
getHostFD()701     int getHostFD() const {
702         return _hostfd;
703     }
704 
getControlFD()705     int getControlFD() const {
706         return _controlfd;
707     }
708 
709     /// ActionScript embedded in a movie can use the built-in
710     /// fscommand() function to send data back to the host
711     /// application.  If you are interested in this data, register
712     /// a handler, which will be called when the embedded scripts
713     /// call fscommand().
714     ///
715     /// The handler gets the MovieClip* that the script is
716     /// embedded in, and the two string arguments passed by the
717     /// script to fscommand().
registerFSCommandCallback(FsCallback * handler)718     DSOEXPORT void registerFSCommandCallback(FsCallback* handler) {
719         _fsCommandHandler = handler;
720     }
721 
722     /// Call this to notify FS commands
723     DSOEXPORT void handleFsCommand(const std::string& cmd,
724             const std::string& arg) const;
725 
726     /// A callback to the GUI (or whatever is listening) for sending
727     /// events and receiving replies. Used for ActionScript interface
728     /// with the gui (Mouse visibility, Stage alignment etc and System
729     /// information, for instance).
730     ///
731     /// See callInterface method
registerEventCallback(HostInterface * handler)732     DSOEXPORT void registerEventCallback(HostInterface* handler) {
733         _interfaceHandler = handler;
734     }
735 
736     /// Call the hosting application without expecting a reply.
737     //
738     /// @param e    The message to send to the interface.
739     void callInterface(const HostInterface::Message& e) const;
740 
741     /// Call the hosting application, ensuring a return of the requested type.
742     //
743     /// If the return type is other than the requested type, this represents
744     /// a bug in the hosting application. An error is logged and the default
745     /// constructed type T is returned. This may cause unexpected
746     /// ActionScript behaviour, but is otherwise safe.
747     //
748     /// @tparam T   The return type expected.
749     /// @param e    The message to send to the interface.
750     template<typename T> T callInterface(const HostInterface::Message& e) const;
751 
752     /// Called from the ScriptLimits tag parser to set the
753     /// global script limits. It is expected behaviour that
754     /// each new loaded movie should override this.
755     /// Can be overridden from gnashrc.
756     //
757     /// @param recursion the maximum number of recursions when
758     ///             finding 'super'.
759     ///             The default value for this (i.e. when no
760     ///             ScriptLimits tag is present) is documented to be
761     ///             256, but this may change and appears not to be
762     ///             crucial for (backward) compatibility.
763     /// @param timeout the timeout in seconds for script execution.
764     ///             The default value for this (i.e. when no
765     ///             ScriptLimits tag is present) is documented to be
766     ///             15 to 20 seconds, depending on platform.
767     void setScriptLimits(std::uint16_t recursion, std::uint16_t timeout);
768 
769     /// Get the current global recursion limit for this movie: it can
770     /// be changed by loaded movies.
getRecursionLimit()771     std::uint16_t getRecursionLimit() const {
772         return _recursionLimit;
773     }
774 
775     /// Get the current global script timeout limit for this movie: it
776     /// can be changed by loaded movies.
getTimeoutLimit()777     std::uint16_t getTimeoutLimit() const
778     {
779         return _timeoutLimit;
780     }
781 
782 #ifdef USE_SWFTREE
783     typedef tree<std::pair<std::string, std::string> > InfoTree;
784     void getMovieInfo(InfoTree& tr, InfoTree::iterator it);
785     void getCharacterTree(InfoTree& tr, InfoTree::iterator it);
786 #endif
787 
runResources()788     const RunResources& runResources() const { return _runResources; }
789 
790     typedef std::map<std::string, as_object*> ExternalCallbackMethods;
791     typedef std::map<std::string, as_object*> ExternalCallbackInstances;
792     ExternalCallbackMethods _externalCallbackMethods;
793     ExternalCallbackInstances _externalCallbackInstances;
794 
795     /// Add an ExternalInterface callback object with an associated name.
796     //
797     /// @param name     Callback name, exposed to host container.
798     /// @param callback ActionScript function to be invoked if the callback
799     ///                 is called.
800     /// @param instance ActionScript Object to be used as "this" instance
801     ///                 inside the callback. ActionScript null value is
802     ///                 allowed.
803     void addExternalCallback(const std::string& name, as_object* callback,
804                              as_object* instance);
805 
806     bool processInvoke(ExternalInterface::invoke_t *);
807 
808     std::string callExternalCallback(const std::string &name,
809                                      const std::vector<as_value>& args);
810 
811     std::string callExternalJavascript(const std::string &name,
812                                        const std::vector<as_value>& args);
813 
814     /// Removes a queued constructor from the execution queue
815     //
816     /// This is used to prevent construction of targets that are placed and
817     /// then removed in skipped frames. Callers are responsible for determining
818     /// whether it should be removed, for instance by checking for an
819     /// onUnload handler.
820     void removeQueuedConstructor(MovieClip* target);
821 
gc()822     GC& gc() {
823         return _gc;
824     }
825 
826     /// Ask the host interface a question.
827     //
828     /// @param what The question to pose.
829     /// @return     The answer (true for yes, false for no).
830     bool queryInterface(const std::string& what) const;
831 
832     /// Set the current stream block for the driving streaming sound.
833     //
834     /// The frame rate will be changed so that it advances only when the
835     /// block for a particular frame is reached. Only one sound can drive the
836     /// frame: the first one to be registered.
837     //
838     /// @param id       The id of the stream; if another stream is already
839     ///                 driving the frame rate, nothing happens.
840     /// @param block    The block of sound currently being played. The current
841     ///                 frame will be advanced or delayed until the frame
842     ///                 corresponding to this block is reached.
843     void setStreamBlock(int id, int block);
844 
845     /// Notify the stage that a sound stream has stopped.
846     //
847     /// If it's the one driving the frame rate, the frame rate will return to
848     /// the nominal rate
849     //
850     /// @param id   The id of the streaming sound.
851     void stopStream(int id);
852 
853 private:
854 
855     /// Set the root movie, replacing the current one if any.
856     //
857     /// This is needed for the cases in which the top-level movie
858     /// is replaced by another movie by effect of a loadMovie call
859     /// or similar.
860     ///
861     /// TODO: inspect what happens about VM version
862     ///   (should the *new* movie drive VM operations?
863     ///    -- hope not ! )
864     ///
865     /// Make sure to call this method before using the movie_root,
866     /// as most operations are delegated to the associated/wrapped
867     /// Movie.
868     ///
869     /// Note that the display viewport will be updated to match
870     /// the size of given movie.
871     ///
872     /// A call to this method is equivalent to a call to setLevel(0, movie).
873     ///
874     /// @param movie
875     /// The Movie to wrap.
876     /// Must have a depth of 0.
877     ///
878     void setRootMovie(Movie* movie);
879 
880     /// Handle mouse events.
881     bool notify_mouse_listeners(const event_id& event);
882 
883     /// This function should return TRUE iff any action triggered
884     /// by the event requires redraw, see \ref events_handling for
885     /// more info.
886     bool fire_mouse_event();
887 
888     /// Take care of dragging, if needed
889     void doMouseDrag();
890 
891     /// Execute expired timers
892     void executeAdvanceCallbacks();
893 
894     /// Execute expired timers
895     void executeTimers();
896 
897     /// Cleanup references to unloaded DisplayObjects and run the GC.
898     void cleanupAndCollect();
899 
900     /// \brief
901     /// Return the topmost entity covering the given point
902     /// and enabled to receive mouse events.
903     //
904     /// Return NULL if no "active" entity is found under the pointer.
905     ///
906     /// Coordinates of the point are given in world coordinate space.
907     /// (twips)
908     ///
909     /// @param x
910     ///     X ordinate of the pointer, in world coordinate space (twips)
911     ///
912     /// @param y
913     ///     Y ordinate of the pointer, in world coordiante space (twips).
914     ///
915     InteractiveObject* getTopmostMouseEntity(std::int32_t x,
916             std::int32_t y) const;
917 
918     /// Delete DisplayObjects removed from the stage
919     /// from the display lists
920     void cleanupDisplayList();
921 
922     /// Advance all non-unloaded live chars
923     void advanceLiveChars();
924 
925     /// Boundaries of the Stage are always world boundaries
926     /// and are only invalidated by changes in the background
927     /// color.
setInvalidated()928     void setInvalidated() { _invalidated = true; }
929 
930     /// Every ::display call clears the invalidated flag
931     //
932     /// See setInvalidated();
933     ///
clearInvalidated()934     void clearInvalidated() { _invalidated = false; }
935 
936     /// An invalidated stage will trigger complete redraw
937     //
938     /// So, this method should return true everytime a complete
939     /// redraw is needed. This is typically only needed when
940     /// the background changes.
941     ///
942     /// See setInvalidated() and clearInvalidated().
943     ///
isInvalidated()944     bool isInvalidated() { return _invalidated; }
945 
946     /// Return the priority level of first action queue containing actions.
947     //
948     /// Scanned in proprity order (lower first)
949     ///
950     size_t minPopulatedPriorityQueue() const;
951 
952     /// Process all actions in the the given queue, till more actions
953     /// are found in lower levels, in which case we have an earlier
954     /// return.
955     size_t processActionQueue(size_t lvl);
956 
processingActions()957     bool processingActions() const {
958         return (_processingActionLevel < PRIORITY_SIZE);
959     }
960 
961     const DisplayObject* findDropTarget(std::int32_t x, std::int32_t y,
962             DisplayObject* dragging) const;
963 
964     void handleActionLimitHit(const std::string& ref);
965 
966     typedef std::forward_list<Button*> ButtonListeners;
967     ButtonListeners _buttonListeners;
968 
969     GC _gc;
970 
971     const RunResources& _runResources;
972 
973     /// This initializes a SharedObjectLibrary, which requires
974     /// _baseURL, so that must be initialized first.
975     VM _vm;
976 
977     /// Registered Interface command handler, if any
978     HostInterface* _interfaceHandler;
979 
980     /// Registered FsCommand handler, if any
981     FsCallback* _fsCommandHandler;
982 
983     /// A list of AdvanceableCharacters
984     //
985     /// This is a list (not a vector) as we want to allow
986     /// ::advance of each element to insert new DisplayObjects before
987     /// the start w/out invalidating iterators scanning the
988     /// list forward for proper movie advancement
989     typedef std::forward_list<MovieClip*> LiveChars;
990 
991     /// The list of advanceable DisplayObject, in placement order
992     LiveChars _liveChars;
993 
994     ActionQueue _actionQueue;
995 
996     /// Process all actions in the queue
997     void processActionQueue();
998 
999     /// Width and height of viewport, in pixels
1000     size_t _stageWidth;
1001     size_t _stageHeight;
1002 
1003     rgba m_background_color;
1004     bool m_background_color_set;
1005 
1006     std::int32_t _mouseX;
1007     std::int32_t _mouseY;
1008 
1009     MouseButtonState  _mouseButtonState;
1010 
1011     /// Objects requesting a callback on every movie_root::advance()
1012     typedef std::set<ActiveRelay*> ObjectCallbacks;
1013     ObjectCallbacks _objectCallbacks;
1014 
1015     LoadCallbacks _loadCallbacks;
1016 
1017     typedef std::map<std::uint32_t, std::unique_ptr<Timer>> TimerMap;
1018 
1019     TimerMap _intervalTimers;
1020 
1021     size_t _lastTimerId;
1022 
1023     /// bit-array for recording the unreleased keys
1024     Keys _unreleasedKeys;
1025 
1026     key::code _lastKeyEvent;
1027 
1028     /// The DisplayObject currently holding focus, or 0 if no focus.
1029     DisplayObject* _currentFocus;
1030 
1031     /// @todo fold this into m_mouse_button_state?
1032     boost::optional<DragState> _dragState;
1033 
1034     typedef std::map<int, MovieClip*> Levels;
1035 
1036     /// The movie instance wrapped by this movie_root
1037     //
1038     /// We keep a pointer to the base MovieClip class
1039     /// to avoid having to replicate all of the base class
1040     /// interface to the Movie class definition
1041     Levels _movies;
1042 
1043     typedef std::map<const SWF::DefinitionTag*, as_function*> RegisteredClasses;
1044     RegisteredClasses _registeredClasses;
1045 
1046     /// The root movie. This is initially the same as getLevel(0) but might
1047     /// change during the run. It will be used to setup and retrive initial
1048     /// stage size
1049     Movie* _rootMovie;
1050 
1051     /// See setInvalidated
1052     bool _invalidated;
1053 
1054     /// This is set to true if execution of scripts
1055     /// aborted due to action limit set or whatever else
1056     bool _disableScripts;
1057     int _processingActionLevel;
1058 
1059     /// filedescriptor to write to for host application requests
1060     //
1061     /// -1 if none
1062     int _hostfd;
1063     int _controlfd;
1064 
1065     /// The display quality of the entire movie.
1066     //
1067     /// This is here, not just in the Renderer, so that AS compatibility
1068     /// does not rely on the presence of a renderer.
1069     Quality _quality;
1070 
1071     /// The alignment of the Stage
1072     std::bitset<4u> _alignMode;
1073 
1074     AllowScriptAccessMode _allowScriptAccess;
1075 
1076     /// Whether to show the menu or not.
1077     bool _showMenu;
1078 
1079     /// The current scaling mode of the Stage.
1080     ScaleMode _scaleMode;
1081 
1082     /// The current state of the Stage (fullscreen or not).
1083     DisplayState _displayState;
1084 
1085     // Maximum number of recursions set in the ScriptLimits tag.
1086     std::uint16_t _recursionLimit;
1087 
1088     // Timeout in seconds for script execution, set in the ScriptLimits tag.
1089     std::uint16_t _timeoutLimit;
1090 
1091     // delay between movie advancement, in milliseconds
1092     size_t _movieAdvancementDelay;
1093 
1094     // time of last movie advancement, in milliseconds
1095     size_t _lastMovieAdvancement;
1096 
1097     /// The number of the last unnamed instance, used to name instances.
1098     size_t _unnamedInstance;
1099 
1100     MovieLoader _movieLoader;
1101 
1102     struct SoundStream {
SoundStreamSoundStream1103         SoundStream(int i, int b) : id(i), block(b) {}
1104         int id;
1105         int block;
1106     };
1107 
1108     boost::optional<SoundStream> _timelineSound;
1109 };
1110 
1111 /// Return true if the given string can be interpreted as a _level name
1112 //
1113 /// @param name
1114 ///   The target string.
1115 ///   Will be considered case-insensitive if VM version is < 7.
1116 ///
1117 /// @param levelno
1118 ///   Output parameter, will be set to the level number, if true is
1119 ///   returned
1120 bool isLevelTarget(int version, const std::string& name, unsigned int& levelno);
1121 
1122 DSOEXPORT short stringToStageAlign(const std::string& s);
1123 
1124 template<typename T>
1125 T
callInterface(const HostInterface::Message & e)1126 movie_root::callInterface(const HostInterface::Message& e) const
1127 {
1128     if (!_interfaceHandler) {
1129         log_error("Hosting application registered no callback for "
1130                 "messages, can't call %s(%s)");
1131         return T();
1132     }
1133 
1134     try {
1135         return boost::any_cast<T>(_interfaceHandler->call(e));
1136     }
1137     catch (const boost::bad_any_cast&) {
1138         log_error(_("Unexpected type from host interface when requesting "
1139                 "%1%"), e);
1140         return T();
1141     }
1142 }
1143 
1144 } // namespace gnash
1145 
1146 #endif // GNASH_MOVIE_ROOT_H
1147 
1148 // Local Variables:
1149 // mode: C++
1150 // indent-tabs-mode: nil
1151 // End:
1152