Despite not being MovieClips, Loader instances appear to get
the same kind of orphan handling - you can instantiate a
Loader and call 'Loader.load' without ever adding it
to a parent, and the loader will still run.
I've changed the movie code to work with a new `DisplayObjectWeak`
enum. Currently, this just supports `MovieClip` and `Loader`,
but it can easily be extended if we ever need other weak display
objects.
This also fixes a bug where we were adding the loaded MovieClip
as a child of the Loader slightly too early.
* Bump bitflags to 2.0.0
* Sprinkle Clone, Copy, Eq, PartialEq, and Debug derives where needed
* Call `bits` on bitflags, as it is now a method
* Switch from `from_bits_truncate` to `from_bits_retain` on bitflags where needed
* Bump h263-rs for the bitflags 2.0.0 dependency
As part of porting to bitflags 2.0.0, see:
https://kodraus.github.io/rust/2022/10/07/bitflags2.html#upgrading-to-2x
When we skip running a frame for a MovieCilp, we skip all
of its children as well. However, this skip 'counts' as
a skip for any children that already wanted to skip their next
frame. For example, say we create three objects in ActionScript,
and arrange them like 'obj1 -> obj2 -> obj3'.
The first 'obj1.enter_frame' call will not run a new frame
for any of the objects, but next time, 'obj1.enter_frame'
will run a new frame for all of the objects.
This fixes jacksmith, which was missing a frame1-framescript
due to 'enter_frame' getting incorrectly run for a deeply
nested child.
The stage alignment settings viewport_scale_factor should *not* be
applied to `Stage.transform.matrix`, which is only ever changed
as a result of explicit modification from ActionScript. Instead,
alignment and scaling are performed a separate step, which is
transparent to ActionScript.
I've implemented this through a new `viewport_matrix` field,
which is used during stage rendering and mouse coordinate
transformation.
This makes Stage3D instances properly scale - previously, they
would render unscaled. The linux standalone Flash Player doesn't
seem to use HiDPI mode, so I didn't realize that this was a bug
until now.
In the process of implementing this, I discovered and fixed a bug
with how we handle changing the viewport size under winit.
Calling `self.window.set_inner_size` does not immediately take
effect (at least on X11) - calling `self.window.inner_size()`
will report the old size until the next resize event.
Since build our Stage matrices from `self.window.inner_size()`
(and start running the SWF) immediately after `RuffleEvent::OnMetadata`,
we would run a few SWF frames with an incorrect viewport size. This
is visible to SWFs that have the scale mode set to "noScale", and
could break SWFs that expect the initial viewport size to be
the movie size. I've fixed this by delaying SWF execution until
we get a Resize event (if `self.window.inner_size()` does not
immediately report the size we set).
I've also renamed these methods to 'avm1_unload' and
'avm1_removed', to make it clear that they don't
apply to AVM2.
This was causing us to incorrectly skip mouse picks,
and remove masks.
This doesn't perfectly match Flash's behavior - I haven't been
able to reproduce the values produces when the DisplayObject
starts out with certain 'Matrix' values (a non-zero 'b' or 'd').
Howver, when the 'b' and 'd' matrix values are both 0, setting
'dobj.rotation = NaN' has no effect on the matrix, while
'dobj.scaleX = NaN' and 'dobj.scaleY = NaN' both treat 'NaN'
as 0 for the purposes of updating the matrix.
This fixes the tack shooter in Bloons Tower Defense 3, which
tries to set 'rotation = NaN' for spawned tacks.
When a MovieClip is an 'orphan' (it has no parent),
it still has frames run (including frame scripts). Some SWFS
like SteamBirds and 'This is the Only Level TOO' rely on this behavior,
so we need to implement it.
The overall idea is straightforward - we keep a global list of
orphan movies, which we add to whenever we unset the parent for a movie.
This list stores weak references for consistency with Flash.
When we run a frame, we process entries in the root movie list,
in addition to the normal recursive processing from the `Stage`.
However, exactly matching Flash's output turned out to be quite tricky.
The particular sequence of calls I make in `run_all_phases_avm2` makes Ruffle
pass two complicated test cases, but there could still be lurking bugs.
This is enough to get SteamBirds to the first level (which doesn't
render due to a different error).
The mouse picking behavior in AVM2 interacts in complicated
ways with `mouseEnabled` and `mouseChildren.` It's sufficiently
different from AVM1 that I decided to split the logic into separate
`mouse_pick_avm1` and `mouse_pick_avm2` methods.
The `mouseChildren` property is now fully implemented.
Additionally, the `click_block` tests now work correctly
under Ruffle.
Combined with the orphan-movie PR, this is enough to make
SteamBirds fully playable (though performance greatly degrades
over a course of a level).
When removing a clip, first check if it has an unload event listener somewhere
it's hierarchy.
If it does, enqueue the removal to happen on the next frame, by moving it to a negative depth.
In future versions of `gc-arena`, the `Debug` impl. of `Gc`
and `GcCell` will print the pointed-to value, which will cause
derived `Debug` impls. to enter an infinite recursion.
As such, this manually implements `Debug` on types wrapping a
`Gc/GcCell` to maintain the current behavior.
This also centralizes all the code we added in the prior commit into `on_construction_complete`, which should be called whenever an AVM2 object finishes construction.
D.O.s removed by the timeline may only be removed from the depth list
(if they were manipulated by AS3 scripts), but their unload method
would still be called, which is wrong.
And make it generic, as a first step towards making it a general-purpose
data structure for the whole codebase. Some potential replacements are:
* `BoundingBox` in `render/src/bounding_box.rs`.
* `BoxBounds` in `core/src/html/dimensions.rs`.
* Parameters to a bunch of `BitmapData` methods in
`core/src/bitmap/bitmap_data.rs`.
Change `set_matrix` and `set_color_transform` to accept owned structs,
instead of references. This allows callers that already have an owned
struct to pass it directly, thus saving an unnecessary borrow + clone.
This also aligns with other methods, such as `set_sound_transform`,
which currently accepts an owned struct.
It was only used to make structs `#[derive(gc_arena::Collect)]`, and
generally it doesn't make much sense that `render` needs to be GC-aware.
So instead annotate `render` fields in `core` with `#[collect(require_static)]`.