avm2: Fire KeyboardEvent.KEY_UP and KeyboardEvent.KEY_DOWN

The 'charCode' and 'keyCode' properties are now implemented
on `KeyboardEvent`

The input injection code we use does not support keyboard events,
so we can't yet write a regression test for this. However,
both 'You need to burn the rope' and 'This is the Only Level TOO'
now properly handle keyboard events with this PR.
This commit is contained in:
Aaron Hill 2022-08-27 18:31:00 -05:00
parent e3e0488ed4
commit c531994b1c
3 changed files with 71 additions and 1 deletions

View File

@ -100,6 +100,7 @@ pub struct SystemClasses<'gc> {
pub illegaloperationerror: ClassObject<'gc>,
pub eventdispatcher: ClassObject<'gc>,
pub rectangle: ClassObject<'gc>,
pub keyboardevent: ClassObject<'gc>,
}
impl<'gc> SystemClasses<'gc> {
@ -169,6 +170,7 @@ impl<'gc> SystemClasses<'gc> {
illegaloperationerror: object,
eventdispatcher: object,
rectangle: object,
keyboardevent: object,
}
}
}
@ -711,6 +713,7 @@ fn load_playerglobal<'gc>(
("flash.events", "Event", event),
("flash.events", "TextEvent", textevent),
("flash.events", "ErrorEvent", errorevent),
("flash.events", "KeyboardEvent", keyboardevent),
("flash.events", "ProgressEvent", progressevent),
("flash.events", "SecurityErrorEvent", securityerrorevent),
("flash.events", "IOErrorEvent", ioerrorevent),

View File

@ -4,11 +4,28 @@ package flash.events
{
public static const KEY_DOWN:String = "keyDown";
public static const KEY_UP:String = "keyUp";
private var _charCode:uint;
private var _keyCode:uint;
public function KeyboardEvent(type:String, bubbles:Boolean = true, cancelable:Boolean = false, charCodeValue:uint = 0, keyCodeValue:uint = 0, keyLocationValue:uint = 0, ctrlKeyValue:Boolean = false, altKeyValue:Boolean = false, shiftKeyValue:Boolean = false)
{
super(type,bubbles,cancelable);
// TODO: fill this up
this._charCode = charCodeValue;
this._keyCode = keyCodeValue;
}
public function get charCode():uint {
return this._charCode;
}
public function set charCode(val:uint) {
this._charCode = val;
}
public function get keyCode():uint {
return this._keyCode;
}
public function set keyCode(val:uint) {
this._keyCode = val;
}
}
}

View File

@ -5,6 +5,7 @@ use crate::avm1::object::Object;
use crate::avm1::property::Attribute;
use crate::avm1::{Avm1, ScriptObject, TObject, Value};
use crate::avm2::object::LoaderInfoObject;
use crate::avm2::object::TObject as _;
use crate::avm2::{
Activation as Avm2Activation, Avm2, CallStack, Domain as Avm2Domain,
EventObject as Avm2EventObject,
@ -907,6 +908,55 @@ impl Player {
}
}
if context.is_action_script_3() {
if let PlayerEvent::KeyDown { key_code, key_char }
| PlayerEvent::KeyUp { key_code, key_char } = event
{
let mut activation = Avm2Activation::from_nothing(context.reborrow());
let event_name = match event {
PlayerEvent::KeyDown { .. } => "keyDown",
PlayerEvent::KeyUp { .. } => "keyUp",
_ => unreachable!(),
};
let keyboardevent_class = activation.avm2().classes().keyboardevent;
let event_name_val: Avm2Value<'_> =
AvmString::new_utf8(activation.context.gc_context, event_name).into();
let keyboard_event = keyboardevent_class
.construct(
&mut activation,
&[
event_name_val,
true.into(), /* bubbles */
false.into(), /* cancelable */
key_char.map_or(0, |c| c as u32).into(), /* charCode */
(key_code as u32).into(), /* keyCode */
],
)
.expect("Failed to construct KeyboardEvent");
let target = activation
.context
.focus_tracker
.get()
.unwrap_or_else(|| activation.context.stage.into())
.object2()
.coerce_to_object(&mut activation)
.expect("DisplayObject is not an object!");
if let Err(e) =
Avm2::dispatch_event(&mut activation.context, keyboard_event, target)
{
log::error!(
"Encountered AVM2 error when broadcasting `{}` event: {}",
event_name,
e
);
}
}
}
// keyPress events take precedence over text input.
if !key_press_handled {
if let PlayerEvent::TextInput { codepoint } = event {