core: Button work

This commit is contained in:
Mike Welsh 2019-08-19 07:53:12 -07:00
parent 91a0272773
commit 0143d9716e
9 changed files with 318 additions and 291 deletions

View File

@ -162,6 +162,9 @@ impl Avm1 {
root: DisplayNode<'gc>,
mut path: &str,
) -> Option<DisplayNode<'gc>> {
if path == "/" {
log::warn!("ROOT");
}
let mut cur_clip = if path.bytes().nth(0).unwrap_or(0) == b'/' {
path = &path[1..];
root
@ -771,8 +774,11 @@ impl Avm1 {
fn play(&mut self, context: &mut ActionContext) -> Result<(), Error> {
let mut display_object = context.active_clip.write(context.gc_context);
let clip = display_object.as_movie_clip_mut().unwrap();
clip.play();
if let Some(clip) = display_object.as_movie_clip_mut() {
clip.play()
} else {
log::warn!("Play failed: Not a MovieClip");
}
Ok(())
}
@ -909,6 +915,7 @@ impl Avm1 {
} else if let Some(clip) =
Avm1::resolve_slash_path(context.start_clip, context.root, target)
{
log::warn!("Path: {}", target);
context.active_clip = clip;
} else {
log::warn!("SetTarget failed: {} not found", target);
@ -931,8 +938,15 @@ impl Avm1 {
fn action_stop(&mut self, context: &mut ActionContext) -> Result<(), Error> {
let mut display_object = context.active_clip.write(context.gc_context);
let clip = display_object.as_movie_clip_mut().unwrap();
clip.stop();
if let None = display_object.as_movie_clip_mut() {
log::warn!("NO");
}
if let Some(clip) = display_object.as_movie_clip_mut() {
clip.stop();
} else {
log::warn!("Stop failed: Not a MovieClip");
}
Ok(())
}

View File

@ -1,5 +1,5 @@
use crate::display_object::{DisplayObject, DisplayObjectBase};
use crate::event::ClipEvent;
use crate::events::ButtonEvent;
use crate::player::{RenderContext, UpdateContext};
use crate::prelude::*;
use std::collections::BTreeMap;
@ -113,18 +113,53 @@ impl<'gc> Button<'gc> {
self.children[i].values_mut()
}
pub fn handle_button_event(
&mut self,
context: &mut crate::player::UpdateContext<'_, 'gc, '_>,
event: ButtonEvent,
) {
let new_state = match event {
ButtonEvent::RollOut => ButtonState::Up,
ButtonEvent::RollOver => ButtonState::Over,
ButtonEvent::Press => ButtonState::Down,
ButtonEvent::Release => ButtonState::Over,
ButtonEvent::KeyPress(key) => {
self.run_actions(context, swf::ButtonActionCondition::KeyPress, Some(key));
self.state
}
};
match (self.state, new_state) {
(ButtonState::Up, ButtonState::Over) => {
self.run_actions(context, swf::ButtonActionCondition::IdleToOverUp, None);
}
(ButtonState::Over, ButtonState::Up) => {
self.run_actions(context, swf::ButtonActionCondition::OverUpToIdle, None);
}
(ButtonState::Over, ButtonState::Down) => {
self.run_actions(context, swf::ButtonActionCondition::OverUpToOverDown, None);
}
(ButtonState::Down, ButtonState::Over) => {
self.run_actions(context, swf::ButtonActionCondition::OverDownToOverUp, None);
}
_ => (),
}
self.state = new_state;
}
fn run_actions(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
condition: swf::ButtonActionCondition,
key_code: Option<u8>,
) {
for action in &self.static_data.actions {
if action.condition == condition && action.key_code == key_code {
// Note that AVM1 buttons run actions relative to their parent, not themselves.
context
.actions
.push((self.parent().unwrap(), action.action_data.clone()));
if let Some(parent) = self.parent() {
for action in &self.static_data.actions {
if action.condition == condition && action.key_code == key_code {
// Note that AVM1 buttons run actions relative to their parent, not themselves.
context.actions.push((parent, action.action_data.clone()));
}
}
}
}
@ -180,45 +215,29 @@ impl<'gc> DisplayObject<'gc> for Button<'gc> {
return true;
}
}
//}
false
}
fn handle_event(
&mut self,
context: &mut crate::player::UpdateContext<'_, 'gc, '_>,
event: ClipEvent,
) {
let new_state = match event {
ClipEvent::RollOut => ButtonState::Up,
ClipEvent::RollOver => ButtonState::Over,
ClipEvent::Press => ButtonState::Down,
ClipEvent::Release => ButtonState::Over,
ClipEvent::KeyPress(key) => {
self.run_actions(context, swf::ButtonActionCondition::KeyPress, Some(key));
self.state
}
_ => self.state,
};
match (self.state, new_state) {
(ButtonState::Up, ButtonState::Over) => {
self.run_actions(context, swf::ButtonActionCondition::IdleToOverUp, None);
}
(ButtonState::Over, ButtonState::Up) => {
self.run_actions(context, swf::ButtonActionCondition::OverUpToIdle, None);
}
(ButtonState::Over, ButtonState::Down) => {
self.run_actions(context, swf::ButtonActionCondition::OverUpToOverDown, None);
}
(ButtonState::Down, ButtonState::Over) => {
self.run_actions(context, swf::ButtonActionCondition::OverDownToOverUp, None);
}
_ => (),
fn mouse_pick(
&self,
self_node: DisplayNode<'gc>,
point: (Twips, Twips),
) -> Option<DisplayNode<'gc>> {
// The button is hovered if the mouse is over any child nodes.
if self.hit_test(point) {
Some(self_node)
} else {
None
}
}
self.state = new_state;
fn as_button(&self) -> Option<&Self> {
Some(self)
}
fn as_button_mut(&mut self) -> Option<&mut Self> {
Some(self)
}
}

View File

@ -95,7 +95,12 @@ pub trait DisplayObject<'gc>: 'gc + Collect {
fn run_post_frame(&mut self, _context: &mut UpdateContext<'_, 'gc, '_>) {}
fn render(&self, _context: &mut RenderContext<'_, 'gc>) {}
fn handle_click(&mut self, _pos: (f32, f32)) {}
fn as_button(&self) -> Option<&crate::button::Button<'gc>> {
None
}
fn as_button_mut(&mut self) -> Option<&mut crate::button::Button<'gc>> {
None
}
fn as_movie_clip(&self) -> Option<&crate::movie_clip::MovieClip<'gc>> {
None
}
@ -110,19 +115,16 @@ pub trait DisplayObject<'gc>: 'gc + Collect {
}
fn box_clone(&self) -> Box<dyn DisplayObject<'gc>>;
fn pick(&self, _: (Twips, Twips)) -> Option<DisplayNode<'gc>> {
None
}
fn hit_test(&self, _: (Twips, Twips)) -> bool {
false
}
fn handle_event(
&mut self,
_context: &mut crate::player::UpdateContext<'_, 'gc, '_>,
_event: crate::event::ClipEvent,
) {
fn mouse_pick(
&self,
_self_node: DisplayNode<'gc>,
_: (Twips, Twips),
) -> Option<DisplayNode<'gc>> {
None
}
}

View File

@ -1,21 +0,0 @@
use swf::Twips;
#[allow(clippy::enum_variant_names)]
#[derive(Debug)]
pub enum Event {
MouseMove { x: Twips, y: Twips },
MouseUp { x: Twips, y: Twips },
MouseDown { x: Twips, y: Twips },
MouseLeft,
}
#[derive(Debug)]
pub enum ClipEvent {
Press,
Release,
RollOut,
RollOver,
KeyPress(KeyCode),
}
type KeyCode = u8;

31
core/src/events.rs Normal file
View File

@ -0,0 +1,31 @@
use swf::Twips;
#[allow(clippy::enum_variant_names)]
#[derive(Debug)]
pub enum PlayerEvent {
MouseMove { x: Twips, y: Twips },
MouseUp { x: Twips, y: Twips },
MouseDown { x: Twips, y: Twips },
MouseLeft,
}
/// The events that an AVM1 button can fire.
///
/// In Flash, these are created using `on` code on the button instance:
/// ```ignore
/// on(release) {
/// trace("Button clicked");
/// }
/// ```
#[derive(Debug)]
pub enum ButtonEvent {
Press,
Release,
RollOut,
RollOver,
KeyPress(KeyCode),
}
/// Flash virtual keycode.
/// TODO: This will eventually move to a separate module.
pub type KeyCode = u8;

View File

@ -6,7 +6,7 @@ mod bounding_box;
mod button;
mod character;
mod color_transform;
mod event;
mod events;
mod font;
mod graphic;
mod library;
@ -22,7 +22,7 @@ mod transform;
pub mod backend;
pub use event::Event;
pub use events::PlayerEvent;
pub use player::Player;
pub use swf;
pub use swf::Color;

View File

@ -55,14 +55,17 @@ impl<'gc> MovieClip<'gc> {
) -> Self {
Self {
base: Default::default(),
static_data: Gc::allocate(gc_context, MovieClipStatic {
id,
tag_stream_start,
tag_stream_len,
total_frames: num_frames,
audio_stream_info: None,
frame_labels: HashMap::new(),
}),
static_data: Gc::allocate(
gc_context,
MovieClipStatic {
id,
tag_stream_start,
tag_stream_len,
total_frames: num_frames,
audio_stream_info: None,
frame_labels: HashMap::new(),
},
),
tag_stream_pos: 0,
is_playing: true,
goto_queue: Vec::new(),
@ -170,7 +173,7 @@ impl<'gc> MovieClip<'gc> {
c: scale_y * cos,
d: -scale_y * sin,
tx: matrix.tx,
ty: matrix.ty,
ty: matrix.ty,
};
}
@ -186,10 +189,7 @@ impl<'gc> MovieClip<'gc> {
.find(|child| child.read().name() == name)
}
pub fn frame_label_to_number(
&self,
frame_label: &str,
) -> Option<FrameNumber> {
pub fn frame_label_to_number(&self, frame_label: &str) -> Option<FrameNumber> {
self.static_data.frame_labels.get(frame_label).copied()
}
@ -224,7 +224,10 @@ impl<'gc> MovieClip<'gc> {
pub fn get_variable(&self, var_name: &str) -> avm1::Value {
// TODO: Value should be Copy (and contain a Cow/GcCell for big objects)
self.variables.get(var_name).unwrap_or(&avm1::Value::Undefined).clone()
self.variables
.get(var_name)
.unwrap_or(&avm1::Value::Undefined)
.clone()
}
pub fn set_variable(&mut self, var_name: &str, value: avm1::Value) {
@ -338,17 +341,31 @@ impl<'gc> DisplayObject<'gc> for MovieClip<'gc> {
TagCode::DefineSound => self.define_sound(context, reader, tag_len),
TagCode::DefineSprite => self.define_sprite(context, reader, tag_len),
TagCode::DefineText => self.define_text(context, reader),
TagCode::FrameLabel => self.frame_label(context, reader, tag_len, cur_frame, &mut static_data),
TagCode::FrameLabel => {
self.frame_label(context, reader, tag_len, cur_frame, &mut static_data)
}
TagCode::JpegTables => self.jpeg_tables(context, reader, tag_len),
TagCode::PlaceObject => self.preload_place_object(context, reader, tag_len, &mut ids, 1),
TagCode::PlaceObject2 => self.preload_place_object(context, reader, tag_len, &mut ids, 2),
TagCode::PlaceObject3 => self.preload_place_object(context, reader, tag_len, &mut ids, 3),
TagCode::PlaceObject4 => self.preload_place_object(context, reader, tag_len, &mut ids, 4),
TagCode::PlaceObject => {
self.preload_place_object(context, reader, tag_len, &mut ids, 1)
}
TagCode::PlaceObject2 => {
self.preload_place_object(context, reader, tag_len, &mut ids, 2)
}
TagCode::PlaceObject3 => {
self.preload_place_object(context, reader, tag_len, &mut ids, 3)
}
TagCode::PlaceObject4 => {
self.preload_place_object(context, reader, tag_len, &mut ids, 4)
}
TagCode::RemoveObject => self.preload_remove_object(context, reader, &mut ids, 1),
TagCode::RemoveObject2 => self.preload_remove_object(context, reader, &mut ids, 2),
TagCode::ShowFrame => self.preload_show_frame(context, reader, &mut cur_frame),
TagCode::SoundStreamHead => self.preload_sound_stream_head(context, reader, &mut static_data, 1),
TagCode::SoundStreamHead2 => self.preload_sound_stream_head(context, reader, &mut static_data, 2),
TagCode::SoundStreamHead => {
self.preload_sound_stream_head(context, reader, &mut static_data, 1)
}
TagCode::SoundStreamHead2 => {
self.preload_sound_stream_head(context, reader, &mut static_data, 2)
}
TagCode::SoundStreamBlock => self.preload_sound_stream_block(context, reader, tag_len),
_ => Ok(()),
};
@ -391,19 +408,17 @@ impl<'gc> DisplayObject<'gc> for MovieClip<'gc> {
context.transform_stack.pop();
}
fn pick(&self, point: (Twips, Twips)) -> Option<DisplayNode<'gc>> {
//if self.world_bounds().contains(point) {
for child in self.children.values().rev() {
if child.read().hit_test(point) {
return Some(*child);
}
let button = child.read().pick(point);
if button.is_some() {
return button;
fn mouse_pick(
&self,
_self_node: DisplayNode<'gc>,
point: (Twips, Twips),
) -> Option<DisplayNode<'gc>> {
for child in self.children.values() {
let result = child.read().mouse_pick(*child, point);
if result.is_some() {
return result;
}
}
//}
None
}
@ -794,8 +809,13 @@ impl<'gc, 'a> MovieClip<'gc> {
) -> DecodeResult {
let id = reader.read_character_id()?;
let num_frames = reader.read_u16()?;
let mut movie_clip =
MovieClip::new_with_data(context.gc_context, id, reader.get_ref().position(), tag_len - 4, num_frames);
let mut movie_clip = MovieClip::new_with_data(
context.gc_context,
id,
reader.get_ref().position(),
tag_len - 4,
num_frames,
);
movie_clip.preload(context);
@ -830,7 +850,11 @@ impl<'gc, 'a> MovieClip<'gc> {
static_data: &mut MovieClipStatic,
) -> DecodeResult {
let frame_label = reader.read_frame_label(tag_len)?;
if static_data.frame_labels.insert(frame_label.label, cur_frame).is_some() {
if static_data
.frame_labels
.insert(frame_label.label, cur_frame)
.is_some()
{
log::warn!("Movie clip {}: Duplicated frame label", self.id());
}
Ok(())
@ -910,7 +934,7 @@ impl<'gc, 'a> MovieClip<'gc> {
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
reader: &mut SwfStream<&'a [u8]>,
tag_len: usize,
tag_len: usize,
version: u8,
) -> DecodeResult {
let place_object = if version == 1 {
@ -1007,7 +1031,7 @@ impl<'gc, 'a> MovieClip<'gc> {
#[inline]
fn remove_object(
&mut self,
_context: &mut UpdateContext<'_, 'gc, '_>,
context: &mut UpdateContext<'_, 'gc, '_>,
reader: &mut SwfStream<&'a [u8]>,
version: u8,
) -> DecodeResult {
@ -1016,7 +1040,9 @@ impl<'gc, 'a> MovieClip<'gc> {
} else {
reader.read_remove_object_2()
}?;
self.children.remove(&remove_object.depth);
if let Some(child) = self.children.remove(&remove_object.depth) {
child.write(context.gc_context).set_parent(None);
}
Ok(())
}
@ -1036,7 +1062,8 @@ impl<'gc, 'a> MovieClip<'gc> {
context: &mut UpdateContext<'_, 'gc, '_>,
_reader: &mut SwfStream<&'a [u8]>,
) -> DecodeResult {
if let (Some(stream_info), None) = (&self.static_data.audio_stream_info, &self.audio_stream) {
if let (Some(stream_info), None) = (&self.static_data.audio_stream_info, &self.audio_stream)
{
let slice = crate::tag_utils::SwfSlice {
data: std::sync::Arc::clone(context.swf_data),
start: self.tag_stream_start() as usize,
@ -1063,7 +1090,6 @@ impl<'gc, 'a> MovieClip<'gc> {
}
}
/// Static data shared between all instances of a movie clip.
#[allow(dead_code)]
#[derive(Clone)]

View File

@ -1,6 +1,6 @@
use crate::avm1::Avm1;
use crate::backend::{audio::AudioBackend, render::RenderBackend};
use crate::event::{ClipEvent, Event};
use crate::events::{ButtonEvent, PlayerEvent};
use crate::library::Library;
use crate::movie_clip::MovieClip;
use crate::prelude::*;
@ -155,9 +155,20 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
self.movie_height
}
pub fn handle_event(&mut self, event: Event) {
pub fn handle_event(&mut self, event: PlayerEvent) {
let mut needs_render = false;
// Update mouse position from mouse events.
if let PlayerEvent::MouseMove { x, y }
| PlayerEvent::MouseDown { x, y }
| PlayerEvent::MouseUp { x, y } = event
{
self.mouse_pos = (x, y);
if self.update_roll_over() {
needs_render = true;
}
}
let (
global_time,
swf_data,
@ -166,7 +177,6 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
renderer,
audio,
avm,
mouse_pos,
is_mouse_down,
) = (
self.global_time,
@ -176,101 +186,62 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
&mut self.renderer,
&mut self.audio,
&mut self.avm,
&mut self.mouse_pos,
&mut self.is_mouse_down,
);
let mut needs_mouse_update = false;
self.gc_arena.mutate(|gc_context, gc_root| {
let actions = {
let mut update_context = UpdateContext {
global_time,
swf_data,
swf_version,
library: gc_root.library.write(gc_context),
background_color,
avm,
renderer,
audio,
actions: vec![],
gc_context,
active_clip: gc_root.root,
};
match event {
Event::MouseMove { x, y } => {
*mouse_pos = (x, y);
needs_mouse_update = true;
}
Event::MouseLeft => {
// TODO: Just setting to a bogus value for now.
*mouse_pos = (Twips::new(std::i32::MAX), Twips::new(std::i32::MAX));
needs_mouse_update = true;
}
Event::MouseDown { x, y } => {
*mouse_pos = (x, y);
*is_mouse_down = true;
needs_render = true;
if let Some(node) = &*gc_root.mouse_hover_node.read() {
update_context.active_clip = *node;
node.write(gc_context)
.handle_event(&mut update_context, ClipEvent::Press);
}
}
Event::MouseUp { x, y } => {
*mouse_pos = (x, y);
*is_mouse_down = false;
needs_render = true;
if let Some(node) = &*gc_root.mouse_hover_node.read() {
if gc_root.root.read().pick(*mouse_pos).map(GcCell::as_ptr)
== Some(node.as_ptr())
{
update_context.active_clip = *node;
node.write(gc_context)
.handle_event(&mut update_context, ClipEvent::Release);
}
}
needs_mouse_update = true;
}
_ => (),
}
update_context.actions
let mut update_context = UpdateContext {
global_time,
swf_data,
swf_version,
library: gc_root.library.write(gc_context),
background_color,
avm,
renderer,
audio,
actions: vec![],
gc_context,
active_clip: gc_root.root,
};
if !actions.is_empty() {
let mut action_context = crate::avm1::ActionContext {
gc_context,
global_time,
root: gc_root.root,
start_clip: gc_root.root,
active_clip: gc_root.root,
audio,
};
for (active_clip, action) in actions {
action_context.start_clip = active_clip;
action_context.active_clip = active_clip;
let _ = avm.do_action(&mut action_context, action.as_ref());
if let Some(node) = &*gc_root.mouse_hover_node.read() {
if let Some(button) = node.write(gc_context).as_button_mut() {
match event {
PlayerEvent::MouseDown { .. } => {
*is_mouse_down = true;
needs_render = true;
update_context.active_clip = *node;
button.handle_button_event(&mut update_context, ButtonEvent::Press);
}
PlayerEvent::MouseUp { .. } => {
*is_mouse_down = false;
needs_render = true;
update_context.active_clip = *node;
button.handle_button_event(&mut update_context, ButtonEvent::Release);
}
_ => (),
}
}
}
Self::run_actions(&mut update_context, gc_root.root);
});
if needs_mouse_update && self.update_mouse() {
needs_render = true;
}
if needs_render {
// Update display after mouse events.
self.render();
}
}
fn update_mouse(&mut self) -> bool {
// Don't update roll-over state when mouse is down.
// TODO: This depends on button tracking.
fn update_roll_over(&mut self) -> bool {
// TODO: While the mouse is down, maintain the hovered node.
if self.is_mouse_down {
return false;
}
let (global_time, swf_data, swf_version, background_color, renderer, audio, avm, mouse_pos) = (
let (global_time, swf_data, swf_version, background_color, renderer, audio, avm) = (
self.global_time,
&mut self.swf_data,
self.swf_version,
@ -278,17 +249,17 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
&mut self.renderer,
&mut self.audio,
&mut self.avm,
self.mouse_pos,
);
let hover_changed = self.gc_arena.mutate(|gc_context, gc_root| {
// Updare hovered display object.
let new_hover_node = gc_root.root.read().pick(mouse_pos);
// Check if the hovered object has changed.
// TODO: Yuck... what's the best way to determine if the pointers are the same?
if gc_root.mouse_hover_node.read().map(GcCell::as_ptr)
!= new_hover_node.map(GcCell::as_ptr)
{
let mouse_pos = &self.mouse_pos;
// Check hovered object.
self.gc_arena.mutate(|gc_context, gc_root| {
let new_hover_node = gc_root
.root
.read()
.mouse_pick(gc_root.root, (mouse_pos.0, mouse_pos.1));
let mut cur_hover_node = gc_root.mouse_hover_node.write(gc_context);
if cur_hover_node.map(GcCell::as_ptr) != new_hover_node.map(GcCell::as_ptr) {
let mut update_context = UpdateContext {
global_time,
swf_data,
@ -303,30 +274,30 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
active_clip: gc_root.root,
};
// Fire rollOut event for previous object.
if let Some(node) = &*gc_root.mouse_hover_node.write(gc_context) {
update_context.active_clip = *node;
node.write(gc_context)
.handle_event(&mut update_context, ClipEvent::RollOut);
// RollOut of previous node.
if let Some(node) = &*cur_hover_node {
if let Some(button) = node.write(gc_context).as_button_mut() {
update_context.active_clip = *node;
button.handle_button_event(&mut update_context, ButtonEvent::RollOut);
}
}
*gc_root.mouse_hover_node.write(gc_context) = new_hover_node;
// Fire rollOver event for new object.
// RollOver on new node.
if let Some(node) = new_hover_node {
update_context.active_clip = node;
node.write(gc_context)
.handle_event(&mut update_context, ClipEvent::RollOver);
if let Some(button) = node.write(gc_context).as_button_mut() {
update_context.active_clip = node;
button.handle_button_event(&mut update_context, ButtonEvent::RollOver);
}
}
*cur_hover_node = new_hover_node;
Self::run_actions(&mut update_context, gc_root.root);
true
} else {
false
}
});
// TODO: render
hover_changed
})
}
fn preload(&mut self) {
@ -371,79 +342,30 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
);
self.gc_arena.mutate(|gc_context, gc_root| {
let mut actions = {
let mut update_context = UpdateContext {
global_time,
swf_data,
swf_version,
library: gc_root.library.write(gc_context),
background_color,
avm,
renderer,
audio,
actions: vec![],
gc_context,
active_clip: gc_root.root,
};
gc_root
.root
.write(gc_context)
.run_frame(&mut update_context);
update_context.actions
let mut update_context = UpdateContext {
global_time,
swf_data,
swf_version,
library: gc_root.library.write(gc_context),
background_color,
avm,
renderer,
audio,
actions: vec![],
gc_context,
active_clip: gc_root.root,
};
// TODO: Loop here because goto-ing a frame can queue up for actions.
// Need to figure out the proper order of operations between ticking a clip
// and running the actions.
loop {
{
let mut action_context = crate::avm1::ActionContext {
gc_context,
global_time,
root: gc_root.root,
start_clip: gc_root.root,
active_clip: gc_root.root,
audio,
};
for (active_clip, action) in actions {
action_context.start_clip = active_clip;
action_context.active_clip = active_clip;
let _ = avm.do_action(&mut action_context, action.as_ref());
}
}
gc_root
.root
.write(gc_context)
.run_frame(&mut update_context);
actions = {
let mut update_context = UpdateContext {
global_time,
swf_data,
swf_version,
library: gc_root.library.write(gc_context),
background_color,
avm,
renderer,
audio,
actions: vec![],
gc_context,
active_clip: gc_root.root,
};
gc_root
.root
.write(gc_context)
.run_post_frame(&mut update_context);
update_context.actions
};
if actions.is_empty() {
break;
}
}
Self::run_actions(&mut update_context, gc_root.root);
});
// Update mouse state (check for new hovered button, etc.)
self.update_mouse();
self.update_roll_over();
}
pub fn render(&mut self) {
@ -484,6 +406,40 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
pub fn renderer_mut(&mut self) -> &mut Renderer {
&mut self.renderer
}
fn run_actions<'gc>(update_context: &mut UpdateContext<'_, 'gc, '_>, root: DisplayNode<'gc>) {
// TODO: Loop here because goto-ing a frame can queue up for actions.
// I think this will eventually be cleaned up;
// Need to figure out the proper order of operations between ticking a clip
// and running the actions.
let mut actions = std::mem::replace(&mut update_context.actions, vec![]);
while !actions.is_empty() {
{
let mut action_context = crate::avm1::ActionContext {
gc_context: update_context.gc_context,
global_time: update_context.global_time,
root,
start_clip: root,
active_clip: root,
audio: update_context.audio,
};
for (active_clip, action) in actions {
action_context.start_clip = active_clip;
action_context.active_clip = active_clip;
let _ = update_context
.avm
.do_action(&mut action_context, action.as_ref());
}
}
// Run goto queues.
update_context.active_clip = root;
root.write(update_context.gc_context)
.run_post_frame(update_context);
actions = std::mem::replace(&mut update_context.actions, vec![]);
}
}
}
pub struct UpdateContext<'a, 'gc, 'gc_context> {

View File

@ -75,7 +75,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
}
WindowEvent::CursorMoved { position, .. } => {
mouse_pos = position;
let event = ruffle_core::Event::MouseMove {
let event = ruffle_core::PlayerEvent::MouseMove {
x: Twips::from_pixels(position.x),
y: Twips::from_pixels(position.y),
};
@ -87,12 +87,12 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
..
} => {
let event = if pressed == ElementState::Pressed {
ruffle_core::Event::MouseDown {
ruffle_core::PlayerEvent::MouseDown {
x: Twips::from_pixels(mouse_pos.x),
y: Twips::from_pixels(mouse_pos.y),
}
} else {
ruffle_core::Event::MouseUp {
ruffle_core::PlayerEvent::MouseUp {
x: Twips::from_pixels(mouse_pos.x),
y: Twips::from_pixels(mouse_pos.y),
}
@ -100,7 +100,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
player.handle_event(event);
}
WindowEvent::CursorLeft { .. } => {
player.handle_event(ruffle_core::Event::MouseLeft)
player.handle_event(ruffle_core::PlayerEvent::MouseLeft)
}
WindowEvent::CloseRequested => request_close = true,
_ => (),