core: Add AVM1 values to debug UI
This commit is contained in:
parent
afdd617d29
commit
aaf9ecfa32
|
@ -28,7 +28,7 @@ mod tests;
|
|||
pub use activation::{Activation, ActivationIdentifier};
|
||||
pub use debug::VariableDumper;
|
||||
pub use error::Error;
|
||||
pub use function::ExecutionReason;
|
||||
pub use function::{Executable, ExecutionReason};
|
||||
pub use globals::context_menu::make_context_menu_state;
|
||||
pub use globals::shared_object::flush;
|
||||
pub use globals::sound::start as start_sound;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
mod avm1;
|
||||
mod display_object;
|
||||
mod handle;
|
||||
mod movie;
|
||||
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::debug_ui::avm1::Avm1ObjectWindow;
|
||||
use crate::debug_ui::display_object::DisplayObjectWindow;
|
||||
use crate::debug_ui::handle::DisplayObjectHandle;
|
||||
use crate::debug_ui::handle::{AVM1ObjectHandle, DisplayObjectHandle};
|
||||
use crate::debug_ui::movie::MovieWindow;
|
||||
use crate::display_object::TDisplayObject;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
|
@ -20,6 +22,7 @@ use weak_table::PtrWeakKeyHashMap;
|
|||
pub struct DebugUi {
|
||||
display_objects: HashMap<DisplayObjectHandle, DisplayObjectWindow>,
|
||||
movies: PtrWeakKeyHashMap<Weak<SwfMovie>, MovieWindow>,
|
||||
avm1_objects: HashMap<AVM1ObjectHandle, Avm1ObjectWindow>,
|
||||
queued_messages: Vec<Message>,
|
||||
}
|
||||
|
||||
|
@ -27,6 +30,7 @@ pub struct DebugUi {
|
|||
pub enum Message {
|
||||
TrackDisplayObject(DisplayObjectHandle),
|
||||
TrackMovie(Arc<SwfMovie>),
|
||||
TrackAVM1Object(AVM1ObjectHandle),
|
||||
TrackStage,
|
||||
TrackTopLevelMovie,
|
||||
}
|
||||
|
@ -40,6 +44,11 @@ impl DebugUi {
|
|||
window.show(egui_ctx, context, object, &mut messages)
|
||||
});
|
||||
|
||||
self.avm1_objects.retain(|object, window| {
|
||||
let object = object.fetch(context.dynamic_root);
|
||||
window.show(egui_ctx, context, object, &mut messages)
|
||||
});
|
||||
|
||||
self.movies
|
||||
.retain(|movie, window| window.show(egui_ctx, context, movie));
|
||||
|
||||
|
@ -55,6 +64,9 @@ impl DebugUi {
|
|||
Message::TrackTopLevelMovie => {
|
||||
self.movies.insert(context.swf.clone(), Default::default());
|
||||
}
|
||||
Message::TrackAVM1Object(object) => {
|
||||
self.avm1_objects.insert(object, Default::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +101,15 @@ impl DebugUi {
|
|||
draw_debug_rect(context, swf::Color::RED, bounds, 5.0);
|
||||
}
|
||||
}
|
||||
|
||||
for (_object, window) in self.avm1_objects.iter() {
|
||||
if let Some(object) = window.hovered_debug_rect() {
|
||||
let object = object.fetch(dynamic_root_set);
|
||||
let bounds = world_matrix * object.world_bounds();
|
||||
|
||||
draw_debug_rect(context, swf::Color::RED, bounds, 5.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
use crate::avm1::{Activation, ActivationIdentifier, Error, Object, TObject, Value};
|
||||
use crate::context::UpdateContext;
|
||||
use crate::debug_ui::display_object::open_display_object_button;
|
||||
use crate::debug_ui::handle::{AVM1ObjectHandle, DisplayObjectHandle};
|
||||
use crate::debug_ui::Message;
|
||||
use egui::{Grid, Id, TextEdit, Ui, Window};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Avm1ObjectWindow {
|
||||
hovered_debug_rect: Option<DisplayObjectHandle>,
|
||||
}
|
||||
|
||||
impl Avm1ObjectWindow {
|
||||
pub fn hovered_debug_rect(&self) -> Option<DisplayObjectHandle> {
|
||||
self.hovered_debug_rect.clone()
|
||||
}
|
||||
|
||||
pub fn show<'gc>(
|
||||
&mut self,
|
||||
egui_ctx: &egui::Context,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
object: Object<'gc>,
|
||||
messages: &mut Vec<Message>,
|
||||
) -> bool {
|
||||
let mut keep_open = true;
|
||||
let base_clip = context.stage.into();
|
||||
let mut activation = Activation::from_nothing(
|
||||
context.reborrow(),
|
||||
ActivationIdentifier::root("Debug"),
|
||||
base_clip,
|
||||
);
|
||||
Window::new(object_name(object))
|
||||
.id(Id::new(object.as_ptr()))
|
||||
.open(&mut keep_open)
|
||||
.scroll2([true, true])
|
||||
.show(egui_ctx, |ui| {
|
||||
Grid::new(ui.id().with("properties"))
|
||||
.num_columns(2)
|
||||
.show(ui, |ui| {
|
||||
let mut keys = object.get_keys(&mut activation, true);
|
||||
keys.sort();
|
||||
|
||||
for key in keys {
|
||||
let value = object.get(key, &mut activation);
|
||||
|
||||
ui.label(key.to_string());
|
||||
show_avm1_value(
|
||||
ui,
|
||||
&mut activation,
|
||||
value,
|
||||
messages,
|
||||
&mut self.hovered_debug_rect,
|
||||
);
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
});
|
||||
keep_open
|
||||
}
|
||||
}
|
||||
|
||||
fn object_name(object: Object) -> String {
|
||||
// TODO: Find a way to give more meaningful names here.
|
||||
// Matching __proto__ to a constant and taking the constants name works, but is super expensive
|
||||
if object.as_executable().is_some() {
|
||||
format!("Function {:p}", object.as_ptr())
|
||||
} else if object.as_array_object().is_some() {
|
||||
format!("Array {:p}", object.as_ptr())
|
||||
} else {
|
||||
format!("Object {:p}", object.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show_avm1_value<'gc>(
|
||||
ui: &mut Ui,
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
value: Result<Value<'gc>, Error<'gc>>,
|
||||
messages: &mut Vec<Message>,
|
||||
hover: &mut Option<DisplayObjectHandle>,
|
||||
) {
|
||||
match value {
|
||||
Ok(Value::Undefined) => {
|
||||
ui.label("Undefined");
|
||||
}
|
||||
Ok(Value::Null) => {
|
||||
ui.label("Null");
|
||||
}
|
||||
Ok(Value::Bool(value)) => {
|
||||
ui.label(value.to_string());
|
||||
}
|
||||
Ok(Value::Number(value)) => {
|
||||
ui.label(value.to_string());
|
||||
}
|
||||
Ok(Value::String(value)) => {
|
||||
TextEdit::singleline(&mut value.to_string()).show(ui);
|
||||
}
|
||||
Ok(Value::Object(value)) => {
|
||||
if value.as_executable().is_some() {
|
||||
ui.label("Function");
|
||||
} else if ui.button(object_name(value)).clicked() {
|
||||
messages.push(Message::TrackAVM1Object(AVM1ObjectHandle::new(
|
||||
&mut activation.context,
|
||||
value,
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(Value::MovieClip(value)) => {
|
||||
if let Some((_, _, object)) = value.resolve_reference(activation) {
|
||||
open_display_object_button(ui, &mut activation.context, messages, object, hover);
|
||||
} else {
|
||||
ui.colored_label(
|
||||
ui.style().visuals.error_fg_color,
|
||||
format!("Unknown movieclip {}", value.path()),
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
ui.colored_label(ui.style().visuals.error_fg_color, e.to_string());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use crate::avm1::TObject;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::debug_ui::handle::DisplayObjectHandle;
|
||||
use crate::debug_ui::handle::{AVM1ObjectHandle, DisplayObjectHandle};
|
||||
use crate::debug_ui::movie::open_movie_button;
|
||||
use crate::debug_ui::Message;
|
||||
use crate::display_object::{DisplayObject, MovieClip, TDisplayObject, TDisplayObjectContainer};
|
||||
|
@ -268,19 +269,37 @@ impl DisplayObjectWindow {
|
|||
.show(ui, |ui| {
|
||||
if let Some(other) = object.parent() {
|
||||
ui.label("Parent");
|
||||
self.display_object_button(ui, context, messages, other);
|
||||
open_display_object_button(
|
||||
ui,
|
||||
context,
|
||||
messages,
|
||||
other,
|
||||
&mut self.hovered_debug_rect,
|
||||
);
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
if let Some(other) = object.masker() {
|
||||
ui.label("Masker");
|
||||
self.display_object_button(ui, context, messages, other);
|
||||
open_display_object_button(
|
||||
ui,
|
||||
context,
|
||||
messages,
|
||||
other,
|
||||
&mut self.hovered_debug_rect,
|
||||
);
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
if let Some(other) = object.maskee() {
|
||||
ui.label("Maskee");
|
||||
self.display_object_button(ui, context, messages, other);
|
||||
open_display_object_button(
|
||||
ui,
|
||||
context,
|
||||
messages,
|
||||
other,
|
||||
&mut self.hovered_debug_rect,
|
||||
);
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
|
@ -322,11 +341,11 @@ impl DisplayObjectWindow {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn show_position(
|
||||
pub fn show_position<'gc>(
|
||||
&mut self,
|
||||
ui: &mut Ui,
|
||||
context: &mut UpdateContext,
|
||||
object: DisplayObject,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
object: DisplayObject<'gc>,
|
||||
messages: &mut Vec<Message>,
|
||||
) {
|
||||
Grid::new(ui.id().with("position"))
|
||||
|
@ -338,6 +357,16 @@ impl DisplayObjectWindow {
|
|||
ui.text_edit_singleline(&mut object.name().to_string());
|
||||
ui.end_row();
|
||||
|
||||
if let crate::avm1::Value::Object(object) = object.object() {
|
||||
ui.label("AVM1 Object");
|
||||
if ui.button(format!("{:p}", object.as_ptr())).clicked() {
|
||||
messages.push(Message::TrackAVM1Object(AVM1ObjectHandle::new(
|
||||
context, object,
|
||||
)));
|
||||
}
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
ui.label("Character");
|
||||
let id = object.id();
|
||||
if let Some(name) =
|
||||
|
@ -461,7 +490,13 @@ impl DisplayObjectWindow {
|
|||
if let Some(ctr) = object.as_container() {
|
||||
CollapsingState::load_with_default_open(ui.ctx(), ui.id().with(object.as_ptr()), false)
|
||||
.show_header(ui, |ui| {
|
||||
self.display_object_button(ui, context, messages, object);
|
||||
open_display_object_button(
|
||||
ui,
|
||||
context,
|
||||
messages,
|
||||
object,
|
||||
&mut self.hovered_debug_rect,
|
||||
);
|
||||
})
|
||||
.body(|ui| {
|
||||
for child in ctr.iter_render_list() {
|
||||
|
@ -469,25 +504,7 @@ impl DisplayObjectWindow {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
self.display_object_button(ui, context, messages, object);
|
||||
}
|
||||
}
|
||||
|
||||
fn display_object_button<'gc>(
|
||||
&mut self,
|
||||
ui: &mut Ui,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
messages: &mut Vec<Message>,
|
||||
object: DisplayObject<'gc>,
|
||||
) {
|
||||
let response = ui.button(summary_name(object));
|
||||
if response.hovered() {
|
||||
self.hovered_debug_rect = Some(DisplayObjectHandle::new(context, object));
|
||||
}
|
||||
if response.clicked() {
|
||||
messages.push(Message::TrackDisplayObject(DisplayObjectHandle::new(
|
||||
context, object,
|
||||
)));
|
||||
open_display_object_button(ui, context, messages, object, &mut self.hovered_debug_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -604,3 +621,21 @@ fn blend_mode_name(mode: BlendMode) -> &'static str {
|
|||
BlendMode::HardLight => "HardLight",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_display_object_button<'gc>(
|
||||
ui: &mut Ui,
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
messages: &mut Vec<Message>,
|
||||
object: DisplayObject<'gc>,
|
||||
hover: &mut Option<DisplayObjectHandle>,
|
||||
) {
|
||||
let response = ui.button(summary_name(object));
|
||||
if response.hovered() {
|
||||
*hover = Some(DisplayObjectHandle::new(context, object));
|
||||
}
|
||||
if response.clicked() {
|
||||
messages.push(Message::TrackDisplayObject(DisplayObjectHandle::new(
|
||||
context, object,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::avm1::TObject;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::display_object::{DisplayObject, DisplayObjectPtr, TDisplayObject};
|
||||
use gc_arena::{DynamicRoot, DynamicRootSet, Rootable};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
// TODO: Make this generic somehow, we'll want AVM1 and AVM2 object handles too
|
||||
// TODO: Make this generic somehow
|
||||
#[derive(Clone)]
|
||||
pub struct DisplayObjectHandle {
|
||||
root: DynamicRoot<Rootable![DisplayObject<'gc>]>,
|
||||
|
@ -50,3 +51,46 @@ impl Hash for DisplayObjectHandle {
|
|||
}
|
||||
|
||||
impl Eq for DisplayObjectHandle {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AVM1ObjectHandle {
|
||||
root: DynamicRoot<Rootable![crate::avm1::Object<'gc>]>,
|
||||
ptr: *const crate::avm1::ObjectPtr,
|
||||
}
|
||||
|
||||
impl AVM1ObjectHandle {
|
||||
pub fn new<'gc>(
|
||||
context: &mut UpdateContext<'_, 'gc>,
|
||||
object: crate::avm1::Object<'gc>,
|
||||
) -> Self {
|
||||
Self {
|
||||
root: context.dynamic_root.stash(context.gc_context, object),
|
||||
ptr: object.as_ptr(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch<'gc>(&self, dynamic_root_set: DynamicRootSet<'gc>) -> crate::avm1::Object<'gc> {
|
||||
*dynamic_root_set.fetch(&self.root)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for AVM1ObjectHandle {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("AVM1ObjectHandle").field(&self.ptr).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<AVM1ObjectHandle> for AVM1ObjectHandle {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &AVM1ObjectHandle) -> bool {
|
||||
self.ptr == other.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for AVM1ObjectHandle {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.ptr.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for AVM1ObjectHandle {}
|
||||
|
|
Loading…
Reference in New Issue