core: Clean up UpdateContext creation
Added Player::mutate_with_update_context, which takes a closure and passes it an UpdateContext.
This commit is contained in:
parent
30ecbd0ecc
commit
12c1bf7cf1
|
@ -480,6 +480,7 @@ mod tests {
|
||||||
renderer: &mut NullRenderer::new(),
|
renderer: &mut NullRenderer::new(),
|
||||||
swf_data: &mut Arc::new(vec![]),
|
swf_data: &mut Arc::new(vec![]),
|
||||||
system_prototypes: avm.prototypes().clone(),
|
system_prototypes: avm.prototypes().clone(),
|
||||||
|
mouse_hovered_object: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let object = ScriptObject::object(gc_context, Some(avm.prototypes().object)).into();
|
let object = ScriptObject::object(gc_context, Some(avm.prototypes().object)).into();
|
||||||
|
|
|
@ -45,6 +45,7 @@ where
|
||||||
renderer: &mut NullRenderer::new(),
|
renderer: &mut NullRenderer::new(),
|
||||||
swf_data: &mut Arc::new(vec![]),
|
swf_data: &mut Arc::new(vec![]),
|
||||||
system_prototypes: avm.prototypes().clone(),
|
system_prototypes: avm.prototypes().clone(),
|
||||||
|
mouse_hovered_object: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let globals = avm.global_object_cell();
|
let globals = avm.global_object_cell();
|
||||||
|
|
|
@ -85,6 +85,9 @@ pub struct UpdateContext<'a, 'gc, 'gc_context> {
|
||||||
/// The current set of system-specified prototypes to use when constructing
|
/// The current set of system-specified prototypes to use when constructing
|
||||||
/// new built-in objects.
|
/// new built-in objects.
|
||||||
pub system_prototypes: avm1::SystemPrototypes<'gc>,
|
pub system_prototypes: avm1::SystemPrototypes<'gc>,
|
||||||
|
|
||||||
|
/// The display object that the mouse is currently hovering over.
|
||||||
|
pub mouse_hovered_object: Option<DisplayObject<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A queued ActionScript call.
|
/// A queued ActionScript call.
|
||||||
|
|
|
@ -28,13 +28,15 @@ struct GcRoot<'gc>(GcCell<'gc, GcRootData<'gc>>);
|
||||||
struct GcRootData<'gc> {
|
struct GcRootData<'gc> {
|
||||||
library: Library<'gc>,
|
library: Library<'gc>,
|
||||||
root: DisplayObject<'gc>,
|
root: DisplayObject<'gc>,
|
||||||
mouse_hover_node: Option<DisplayObject<'gc>>, // TODO: Remove GcCell wrapped inside GcCell.
|
mouse_hovered_object: Option<DisplayObject<'gc>>, // TODO: Remove GcCell wrapped inside GcCell.
|
||||||
avm: Avm1<'gc>,
|
avm: Avm1<'gc>,
|
||||||
action_queue: ActionQueue<'gc>,
|
action_queue: ActionQueue<'gc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> GcRootData<'gc> {
|
impl<'gc> GcRootData<'gc> {
|
||||||
fn get(
|
/// Splits out parameters for creating an `UpdateContext`
|
||||||
|
/// (because we can borrow fields of `self` independently)
|
||||||
|
fn update_context_params(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> (
|
) -> (
|
||||||
DisplayObject<'gc>,
|
DisplayObject<'gc>,
|
||||||
|
@ -188,7 +190,7 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
header.num_frames,
|
header.num_frames,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
mouse_hover_node: None,
|
mouse_hovered_object: None,
|
||||||
avm: Avm1::new(gc_context, NEWEST_PLAYER_VERSION),
|
avm: Avm1::new(gc_context, NEWEST_PLAYER_VERSION),
|
||||||
action_queue: ActionQueue::new(),
|
action_queue: ActionQueue::new(),
|
||||||
},
|
},
|
||||||
|
@ -303,7 +305,6 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
|
|
||||||
pub fn handle_event(&mut self, event: PlayerEvent) {
|
pub fn handle_event(&mut self, event: PlayerEvent) {
|
||||||
let mut needs_render = false;
|
let mut needs_render = false;
|
||||||
let player_version = self.player_version;
|
|
||||||
|
|
||||||
// Update mouse position from mouse events.
|
// Update mouse position from mouse events.
|
||||||
if let PlayerEvent::MouseMove { x, y }
|
if let PlayerEvent::MouseMove { x, y }
|
||||||
|
@ -317,68 +318,23 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (
|
let mut is_mouse_down = self.is_mouse_down;
|
||||||
global_time,
|
self.mutate_with_update_context(|avm, context| {
|
||||||
swf_data,
|
if let Some(node) = context.mouse_hovered_object {
|
||||||
swf_version,
|
|
||||||
background_color,
|
|
||||||
renderer,
|
|
||||||
audio,
|
|
||||||
navigator,
|
|
||||||
rng,
|
|
||||||
is_mouse_down,
|
|
||||||
) = (
|
|
||||||
self.global_time,
|
|
||||||
&mut self.swf_data,
|
|
||||||
self.swf_version,
|
|
||||||
&mut self.background_color,
|
|
||||||
&mut self.renderer,
|
|
||||||
&mut self.audio,
|
|
||||||
&mut self.navigator,
|
|
||||||
&mut self.rng,
|
|
||||||
&mut self.is_mouse_down,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
|
||||||
let mut root_data = gc_root.0.write(gc_context);
|
|
||||||
let mouse_hover_node = root_data.mouse_hover_node;
|
|
||||||
let (root, library, action_queue, mut avm) = root_data.get();
|
|
||||||
let mut update_context = UpdateContext {
|
|
||||||
player_version,
|
|
||||||
global_time,
|
|
||||||
swf_data,
|
|
||||||
swf_version,
|
|
||||||
library,
|
|
||||||
background_color,
|
|
||||||
rng,
|
|
||||||
renderer,
|
|
||||||
audio,
|
|
||||||
navigator,
|
|
||||||
action_queue,
|
|
||||||
gc_context,
|
|
||||||
root,
|
|
||||||
active_clip: root,
|
|
||||||
start_clip: root,
|
|
||||||
target_clip: Some(root),
|
|
||||||
target_path: Value::Undefined,
|
|
||||||
system_prototypes: avm.prototypes().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(node) = mouse_hover_node {
|
|
||||||
if let Some(button) = node.clone().as_button_mut() {
|
if let Some(button) = node.clone().as_button_mut() {
|
||||||
match event {
|
match event {
|
||||||
PlayerEvent::MouseDown { .. } => {
|
PlayerEvent::MouseDown { .. } => {
|
||||||
*is_mouse_down = true;
|
is_mouse_down = true;
|
||||||
needs_render = true;
|
needs_render = true;
|
||||||
update_context.active_clip = node;
|
context.active_clip = node;
|
||||||
button.handle_button_event(&mut update_context, ButtonEvent::Press);
|
button.handle_button_event(context, ButtonEvent::Press);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerEvent::MouseUp { .. } => {
|
PlayerEvent::MouseUp { .. } => {
|
||||||
*is_mouse_down = false;
|
is_mouse_down = false;
|
||||||
needs_render = true;
|
needs_render = true;
|
||||||
update_context.active_clip = node;
|
context.active_clip = node;
|
||||||
button.handle_button_event(&mut update_context, ButtonEvent::Release);
|
button.handle_button_event(context, ButtonEvent::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -386,9 +342,9 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::run_actions(&mut avm, &mut update_context);
|
Self::run_actions(avm, context);
|
||||||
});
|
});
|
||||||
|
self.is_mouse_down = is_mouse_down;
|
||||||
if needs_render {
|
if needs_render {
|
||||||
// Update display after mouse events.
|
// Update display after mouse events.
|
||||||
self.render();
|
self.render();
|
||||||
|
@ -396,73 +352,36 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_roll_over(&mut self) -> bool {
|
fn update_roll_over(&mut self) -> bool {
|
||||||
let player_version = self.player_version;
|
|
||||||
// TODO: While the mouse is down, maintain the hovered node.
|
// TODO: While the mouse is down, maintain the hovered node.
|
||||||
if self.is_mouse_down {
|
if self.is_mouse_down {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
let mouse_pos = self.mouse_pos;
|
||||||
let (global_time, swf_data, swf_version, background_color, renderer, audio, navigator, rng) = (
|
|
||||||
self.global_time,
|
|
||||||
&mut self.swf_data,
|
|
||||||
self.swf_version,
|
|
||||||
&mut self.background_color,
|
|
||||||
&mut self.renderer,
|
|
||||||
&mut self.audio,
|
|
||||||
&mut self.navigator,
|
|
||||||
&mut self.rng,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mouse_pos = &self.mouse_pos;
|
|
||||||
// Check hovered object.
|
// Check hovered object.
|
||||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
self.mutate_with_update_context(|avm, context| {
|
||||||
let mut root_data = gc_root.0.write(gc_context);
|
let root = context.root;
|
||||||
let new_hover_node = root_data
|
let new_hovered = root.mouse_pick(root, (mouse_pos.0, mouse_pos.1));
|
||||||
.root
|
let cur_hovered = context.mouse_hovered_object;
|
||||||
.mouse_pick(root_data.root, (mouse_pos.0, mouse_pos.1));
|
if cur_hovered.map(|d| d.as_ptr()) != new_hovered.map(|d| d.as_ptr()) {
|
||||||
let cur_hover_node = root_data.mouse_hover_node;
|
|
||||||
let (root, library, action_queue, avm) = root_data.get();
|
|
||||||
if cur_hover_node.map(|d| d.as_ptr()) != new_hover_node.map(|d| d.as_ptr()) {
|
|
||||||
let mut update_context = UpdateContext {
|
|
||||||
player_version,
|
|
||||||
global_time,
|
|
||||||
swf_data,
|
|
||||||
swf_version,
|
|
||||||
library,
|
|
||||||
background_color,
|
|
||||||
rng,
|
|
||||||
renderer,
|
|
||||||
audio,
|
|
||||||
navigator,
|
|
||||||
action_queue,
|
|
||||||
gc_context,
|
|
||||||
active_clip: root,
|
|
||||||
start_clip: root,
|
|
||||||
target_clip: Some(root),
|
|
||||||
root,
|
|
||||||
target_path: Value::Undefined,
|
|
||||||
system_prototypes: avm.prototypes().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// RollOut of previous node.
|
// RollOut of previous node.
|
||||||
if let Some(mut node) = cur_hover_node {
|
if let Some(mut node) = cur_hovered {
|
||||||
if let Some(mut button) = node.as_button_mut().copied() {
|
if let Some(mut button) = node.as_button_mut().copied() {
|
||||||
update_context.active_clip = node;
|
context.active_clip = node;
|
||||||
button.handle_button_event(&mut update_context, ButtonEvent::RollOut);
|
button.handle_button_event(context, ButtonEvent::RollOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RollOver on new node.
|
// RollOver on new node.
|
||||||
if let Some(mut node) = new_hover_node {
|
if let Some(mut node) = new_hovered {
|
||||||
if let Some(mut button) = node.as_button_mut().copied() {
|
if let Some(mut button) = node.as_button_mut().copied() {
|
||||||
update_context.active_clip = node;
|
context.active_clip = node;
|
||||||
button.handle_button_event(&mut update_context, ButtonEvent::RollOver);
|
button.handle_button_event(context, ButtonEvent::RollOver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO cur_hover_node = new_hover_node;
|
context.mouse_hovered_object = new_hovered;
|
||||||
|
|
||||||
Self::run_actions(avm, &mut update_context);
|
Self::run_actions(avm, context);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -471,61 +390,17 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preload(&mut self) {
|
fn preload(&mut self) {
|
||||||
let (
|
self.mutate_with_update_context(|_avm, context| {
|
||||||
player_version,
|
|
||||||
global_time,
|
|
||||||
swf_data,
|
|
||||||
swf_version,
|
|
||||||
background_color,
|
|
||||||
renderer,
|
|
||||||
audio,
|
|
||||||
navigator,
|
|
||||||
rng,
|
|
||||||
) = (
|
|
||||||
self.player_version,
|
|
||||||
self.global_time,
|
|
||||||
&mut self.swf_data,
|
|
||||||
self.swf_version,
|
|
||||||
&mut self.background_color,
|
|
||||||
&mut self.renderer,
|
|
||||||
&mut self.audio,
|
|
||||||
&mut self.navigator,
|
|
||||||
&mut self.rng,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
|
||||||
let mut root_data = gc_root.0.write(gc_context);
|
|
||||||
let (mut root, library, action_queue, avm) = root_data.get();
|
|
||||||
let mut update_context = UpdateContext {
|
|
||||||
player_version,
|
|
||||||
global_time,
|
|
||||||
swf_data,
|
|
||||||
swf_version,
|
|
||||||
library,
|
|
||||||
background_color,
|
|
||||||
rng,
|
|
||||||
renderer,
|
|
||||||
audio,
|
|
||||||
navigator,
|
|
||||||
action_queue,
|
|
||||||
gc_context,
|
|
||||||
active_clip: root,
|
|
||||||
start_clip: root,
|
|
||||||
target_clip: Some(root),
|
|
||||||
root,
|
|
||||||
target_path: Value::Undefined,
|
|
||||||
system_prototypes: avm.prototypes().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut morph_shapes = fnv::FnvHashMap::default();
|
let mut morph_shapes = fnv::FnvHashMap::default();
|
||||||
|
let mut root = context.root;
|
||||||
root.as_movie_clip_mut()
|
root.as_movie_clip_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.preload(&mut update_context, &mut morph_shapes);
|
.preload(context, &mut morph_shapes);
|
||||||
|
|
||||||
// Finalize morph shapes.
|
// Finalize morph shapes.
|
||||||
for (id, static_data) in morph_shapes {
|
for (id, static_data) in morph_shapes {
|
||||||
let morph_shape = MorphShape::new(gc_context, static_data);
|
let morph_shape = MorphShape::new(context.gc_context, static_data);
|
||||||
update_context.library.register_character(
|
context.library.register_character(
|
||||||
id,
|
id,
|
||||||
crate::character::Character::MorphShape(Box::new(morph_shape)),
|
crate::character::Character::MorphShape(Box::new(morph_shape)),
|
||||||
);
|
);
|
||||||
|
@ -534,55 +409,10 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_frame(&mut self) {
|
pub fn run_frame(&mut self) {
|
||||||
let (
|
self.mutate_with_update_context(|avm, context| {
|
||||||
player_version,
|
let mut root = context.root;
|
||||||
global_time,
|
root.run_frame(context);
|
||||||
swf_data,
|
Self::run_actions(avm, context);
|
||||||
swf_version,
|
|
||||||
background_color,
|
|
||||||
renderer,
|
|
||||||
audio,
|
|
||||||
navigator,
|
|
||||||
rng,
|
|
||||||
) = (
|
|
||||||
self.player_version,
|
|
||||||
self.global_time,
|
|
||||||
&mut self.swf_data,
|
|
||||||
self.swf_version,
|
|
||||||
&mut self.background_color,
|
|
||||||
&mut self.renderer,
|
|
||||||
&mut self.audio,
|
|
||||||
&mut self.navigator,
|
|
||||||
&mut self.rng,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
|
||||||
let mut root_data = gc_root.0.write(gc_context);
|
|
||||||
let (mut root, library, action_queue, avm) = root_data.get();
|
|
||||||
let mut update_context = UpdateContext {
|
|
||||||
player_version,
|
|
||||||
global_time,
|
|
||||||
swf_data,
|
|
||||||
swf_version,
|
|
||||||
library,
|
|
||||||
background_color,
|
|
||||||
rng,
|
|
||||||
renderer,
|
|
||||||
audio,
|
|
||||||
navigator,
|
|
||||||
action_queue,
|
|
||||||
gc_context,
|
|
||||||
active_clip: root,
|
|
||||||
start_clip: root,
|
|
||||||
target_clip: Some(root),
|
|
||||||
root,
|
|
||||||
target_path: Value::Undefined,
|
|
||||||
system_prototypes: avm.prototypes().clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
root.run_frame(&mut update_context);
|
|
||||||
|
|
||||||
Self::run_actions(avm, &mut update_context);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update mouse state (check for new hovered button, etc.)
|
// Update mouse state (check for new hovered button, etc.)
|
||||||
|
@ -714,6 +544,70 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs the closure `f` with an `UpdateContext`.
|
||||||
|
/// This takes cares of populating the `UpdateContext` struct, avoiding borrow issues.
|
||||||
|
fn mutate_with_update_context<F, R>(&mut self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: for<'a, 'gc> FnOnce(&mut Avm1<'gc>, &mut UpdateContext<'a, 'gc, '_>) -> R,
|
||||||
|
{
|
||||||
|
// We have to do this piecewise borrowing of fields before the closure to avoid
|
||||||
|
// completely borrowing `self`.
|
||||||
|
let (
|
||||||
|
player_version,
|
||||||
|
global_time,
|
||||||
|
swf_data,
|
||||||
|
swf_version,
|
||||||
|
background_color,
|
||||||
|
renderer,
|
||||||
|
audio,
|
||||||
|
navigator,
|
||||||
|
rng,
|
||||||
|
) = (
|
||||||
|
self.player_version,
|
||||||
|
self.global_time,
|
||||||
|
&mut self.swf_data,
|
||||||
|
self.swf_version,
|
||||||
|
&mut self.background_color,
|
||||||
|
&mut self.renderer,
|
||||||
|
&mut self.audio,
|
||||||
|
&mut self.navigator,
|
||||||
|
&mut self.rng,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.gc_arena.mutate(|gc_context, gc_root| {
|
||||||
|
let mut root_data = gc_root.0.write(gc_context);
|
||||||
|
let mouse_hovered_object = root_data.mouse_hovered_object;
|
||||||
|
let (root, library, action_queue, avm) = root_data.update_context_params();
|
||||||
|
let mut update_context = UpdateContext {
|
||||||
|
player_version,
|
||||||
|
global_time,
|
||||||
|
swf_data,
|
||||||
|
swf_version,
|
||||||
|
library,
|
||||||
|
background_color,
|
||||||
|
rng,
|
||||||
|
renderer,
|
||||||
|
audio,
|
||||||
|
navigator,
|
||||||
|
action_queue,
|
||||||
|
gc_context,
|
||||||
|
active_clip: root,
|
||||||
|
start_clip: root,
|
||||||
|
target_clip: Some(root),
|
||||||
|
root,
|
||||||
|
target_path: Value::Undefined,
|
||||||
|
system_prototypes: avm.prototypes().clone(),
|
||||||
|
mouse_hovered_object,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = f(avm, &mut update_context);
|
||||||
|
|
||||||
|
// Hovered object may have been updated; copy it back to the GC root.
|
||||||
|
root_data.mouse_hovered_object = update_context.mouse_hovered_object;
|
||||||
|
ret
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads font data from the given buffer.
|
/// Loads font data from the given buffer.
|
||||||
/// The buffer should be the `DefineFont3` info for the tag.
|
/// The buffer should be the `DefineFont3` info for the tag.
|
||||||
/// The tag header should not be included.
|
/// The tag header should not be included.
|
||||||
|
|
Loading…
Reference in New Issue