mirror of
https://github.com/servo/servo.git
synced 2025-09-30 08:39:16 +01:00
script: Move gamepad DOM interfaces to script/dom/gamepad/
(#38900)
Moves interfaces defined by the gamepad spec to the `script/dom/gamepad/` module from `script/dom/`. Testing: Just a refactor shouldn't need any testing Fixes: N/A Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
parent
21a7782206
commit
461ff26812
10 changed files with 26 additions and 18 deletions
313
components/script/dom/gamepad/gamepad.rs
Normal file
313
components/script/dom/gamepad/gamepad.rs
Normal file
|
@ -0,0 +1,313 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::{GamepadSupportedHapticEffects, GamepadUpdateType};
|
||||
use js::typedarray::{Float64, Float64Array};
|
||||
|
||||
use super::gamepadbuttonlist::GamepadButtonList;
|
||||
use super::gamepadhapticactuator::GamepadHapticActuator;
|
||||
use super::gamepadpose::GamepadPose;
|
||||
use crate::dom::bindings::buffer_source::HeapBufferSource;
|
||||
use crate::dom::bindings::codegen::Bindings::GamepadBinding::{GamepadHand, GamepadMethods};
|
||||
use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::gamepadevent::{GamepadEvent, GamepadEventType};
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::window::Window;
|
||||
use crate::script_runtime::{CanGc, JSContext};
|
||||
|
||||
// This value is for determining when to consider a gamepad as having a user gesture
|
||||
// from an axis tilt. This matches the threshold in Chromium.
|
||||
const AXIS_TILT_THRESHOLD: f64 = 0.5;
|
||||
// This value is for determining when to consider a non-digital button "pressed".
|
||||
// Like Gecko and Chromium it derives from the XInput trigger threshold.
|
||||
const BUTTON_PRESS_THRESHOLD: f64 = 30.0 / 255.0;
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct Gamepad {
|
||||
reflector_: Reflector,
|
||||
gamepad_id: u32,
|
||||
id: String,
|
||||
index: Cell<i32>,
|
||||
connected: Cell<bool>,
|
||||
timestamp: Cell<f64>,
|
||||
mapping_type: String,
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
axes: HeapBufferSource<Float64>,
|
||||
buttons: Dom<GamepadButtonList>,
|
||||
pose: Option<Dom<GamepadPose>>,
|
||||
#[ignore_malloc_size_of = "Defined in rust-webvr"]
|
||||
hand: GamepadHand,
|
||||
axis_bounds: (f64, f64),
|
||||
button_bounds: (f64, f64),
|
||||
exposed: Cell<bool>,
|
||||
vibration_actuator: Dom<GamepadHapticActuator>,
|
||||
}
|
||||
|
||||
impl Gamepad {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new_inherited(
|
||||
gamepad_id: u32,
|
||||
id: String,
|
||||
index: i32,
|
||||
connected: bool,
|
||||
timestamp: f64,
|
||||
mapping_type: String,
|
||||
buttons: &GamepadButtonList,
|
||||
pose: Option<&GamepadPose>,
|
||||
hand: GamepadHand,
|
||||
axis_bounds: (f64, f64),
|
||||
button_bounds: (f64, f64),
|
||||
vibration_actuator: &GamepadHapticActuator,
|
||||
) -> Gamepad {
|
||||
Self {
|
||||
reflector_: Reflector::new(),
|
||||
gamepad_id,
|
||||
id,
|
||||
index: Cell::new(index),
|
||||
connected: Cell::new(connected),
|
||||
timestamp: Cell::new(timestamp),
|
||||
mapping_type,
|
||||
axes: HeapBufferSource::default(),
|
||||
buttons: Dom::from_ref(buttons),
|
||||
pose: pose.map(Dom::from_ref),
|
||||
hand,
|
||||
axis_bounds,
|
||||
button_bounds,
|
||||
exposed: Cell::new(false),
|
||||
vibration_actuator: Dom::from_ref(vibration_actuator),
|
||||
}
|
||||
}
|
||||
|
||||
/// When we construct a new gamepad, we initialize the number of buttons and
|
||||
/// axes corresponding to the "standard" gamepad mapping.
|
||||
/// The spec says UAs *may* do this for fingerprint mitigation, and it also
|
||||
/// happens to simplify implementation
|
||||
/// <https://www.w3.org/TR/gamepad/#fingerprinting-mitigation>
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn new(
|
||||
window: &Window,
|
||||
gamepad_id: u32,
|
||||
id: String,
|
||||
mapping_type: String,
|
||||
axis_bounds: (f64, f64),
|
||||
button_bounds: (f64, f64),
|
||||
supported_haptic_effects: GamepadSupportedHapticEffects,
|
||||
xr: bool,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<Gamepad> {
|
||||
let button_list = GamepadButtonList::init_buttons(window, can_gc);
|
||||
let vibration_actuator =
|
||||
GamepadHapticActuator::new(window, gamepad_id, supported_haptic_effects, can_gc);
|
||||
let index = if xr { -1 } else { 0 };
|
||||
let gamepad = reflect_dom_object(
|
||||
Box::new(Gamepad::new_inherited(
|
||||
gamepad_id,
|
||||
id,
|
||||
index,
|
||||
true,
|
||||
0.,
|
||||
mapping_type,
|
||||
&button_list,
|
||||
None,
|
||||
GamepadHand::_empty,
|
||||
axis_bounds,
|
||||
button_bounds,
|
||||
&vibration_actuator,
|
||||
)),
|
||||
window,
|
||||
can_gc,
|
||||
);
|
||||
gamepad.init_axes(can_gc);
|
||||
gamepad
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadMethods<crate::DomTypeHolder> for Gamepad {
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-id
|
||||
fn Id(&self) -> DOMString {
|
||||
DOMString::from(self.id.clone())
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-index
|
||||
fn Index(&self) -> i32 {
|
||||
self.index.get()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-connected
|
||||
fn Connected(&self) -> bool {
|
||||
self.connected.get()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-timestamp
|
||||
fn Timestamp(&self) -> Finite<f64> {
|
||||
Finite::wrap(self.timestamp.get())
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-mapping
|
||||
fn Mapping(&self) -> DOMString {
|
||||
DOMString::from(self.mapping_type.clone())
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-axes
|
||||
fn Axes(&self, _cx: JSContext) -> Float64Array {
|
||||
self.axes
|
||||
.get_typed_array()
|
||||
.expect("Failed to get gamepad axes.")
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-buttons
|
||||
fn Buttons(&self) -> DomRoot<GamepadButtonList> {
|
||||
DomRoot::from_ref(&*self.buttons)
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-vibrationactuator
|
||||
fn VibrationActuator(&self) -> DomRoot<GamepadHapticActuator> {
|
||||
DomRoot::from_ref(&*self.vibration_actuator)
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#gamepadhand-enum
|
||||
fn Hand(&self) -> GamepadHand {
|
||||
self.hand
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepad-pose
|
||||
fn GetPose(&self) -> Option<DomRoot<GamepadPose>> {
|
||||
self.pose.as_ref().map(|p| DomRoot::from_ref(&**p))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Gamepad {
|
||||
pub(crate) fn gamepad_id(&self) -> u32 {
|
||||
self.gamepad_id
|
||||
}
|
||||
|
||||
pub(crate) fn update_connected(&self, connected: bool, has_gesture: bool, can_gc: CanGc) {
|
||||
if self.connected.get() == connected {
|
||||
return;
|
||||
}
|
||||
self.connected.set(connected);
|
||||
|
||||
let event_type = if connected {
|
||||
GamepadEventType::Connected
|
||||
} else {
|
||||
GamepadEventType::Disconnected
|
||||
};
|
||||
|
||||
if has_gesture {
|
||||
self.notify_event(event_type, can_gc);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn index(&self) -> i32 {
|
||||
self.index.get()
|
||||
}
|
||||
|
||||
pub(crate) fn update_index(&self, index: i32) {
|
||||
self.index.set(index);
|
||||
}
|
||||
|
||||
pub(crate) fn update_timestamp(&self, timestamp: f64) {
|
||||
self.timestamp.set(timestamp);
|
||||
}
|
||||
|
||||
pub(crate) fn notify_event(&self, event_type: GamepadEventType, can_gc: CanGc) {
|
||||
let event =
|
||||
GamepadEvent::new_with_type(self.global().as_window(), event_type, self, can_gc);
|
||||
event
|
||||
.upcast::<Event>()
|
||||
.fire(self.global().as_window().upcast::<EventTarget>(), can_gc);
|
||||
}
|
||||
|
||||
/// Initialize the number of axes in the "standard" gamepad mapping.
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-initializing-axes>
|
||||
fn init_axes(&self, can_gc: CanGc) {
|
||||
let initial_axes: Vec<f64> = vec![
|
||||
0., // Horizontal axis for left stick (negative left/positive right)
|
||||
0., // Vertical axis for left stick (negative up/positive down)
|
||||
0., // Horizontal axis for right stick (negative left/positive right)
|
||||
0., // Vertical axis for right stick (negative up/positive down)
|
||||
];
|
||||
self.axes
|
||||
.set_data(GlobalScope::get_cx(), &initial_axes, can_gc)
|
||||
.expect("Failed to set axes data on gamepad.")
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-map-and-normalize-axes>
|
||||
pub(crate) fn map_and_normalize_axes(&self, axis_index: usize, value: f64) {
|
||||
// Let normalizedValue be 2 (logicalValue − logicalMinimum) / (logicalMaximum − logicalMinimum) − 1.
|
||||
let numerator = value - self.axis_bounds.0;
|
||||
let denominator = self.axis_bounds.1 - self.axis_bounds.0;
|
||||
if denominator != 0.0 && denominator.is_finite() {
|
||||
let normalized_value: f64 = 2.0 * numerator / denominator - 1.0;
|
||||
if normalized_value.is_finite() {
|
||||
let mut axis_vec = self
|
||||
.axes
|
||||
.typed_array_to_option()
|
||||
.expect("Axes have not been initialized!");
|
||||
unsafe {
|
||||
axis_vec.as_mut_slice()[axis_index] = normalized_value;
|
||||
}
|
||||
} else {
|
||||
warn!("Axis value is not finite!");
|
||||
}
|
||||
} else {
|
||||
warn!("Axis bounds difference is either 0 or non-finite!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-map-and-normalize-buttons>
|
||||
pub(crate) fn map_and_normalize_buttons(&self, button_index: usize, value: f64) {
|
||||
// Let normalizedValue be (logicalValue − logicalMinimum) / (logicalMaximum − logicalMinimum).
|
||||
let numerator = value - self.button_bounds.0;
|
||||
let denominator = self.button_bounds.1 - self.button_bounds.0;
|
||||
if denominator != 0.0 && denominator.is_finite() {
|
||||
let normalized_value: f64 = numerator / denominator;
|
||||
if normalized_value.is_finite() {
|
||||
let pressed = normalized_value >= BUTTON_PRESS_THRESHOLD;
|
||||
// TODO: Determine a way of getting touch capability for button
|
||||
if let Some(button) = self.buttons.IndexedGetter(button_index as u32) {
|
||||
button.update(pressed, /*touched*/ pressed, normalized_value);
|
||||
}
|
||||
} else {
|
||||
warn!("Button value is not finite!");
|
||||
}
|
||||
} else {
|
||||
warn!("Button bounds difference is either 0 or non-finite!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-exposed>
|
||||
pub(crate) fn exposed(&self) -> bool {
|
||||
self.exposed.get()
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-exposed>
|
||||
pub(crate) fn set_exposed(&self, exposed: bool) {
|
||||
self.exposed.set(exposed);
|
||||
}
|
||||
|
||||
pub(crate) fn vibration_actuator(&self) -> &GamepadHapticActuator {
|
||||
&self.vibration_actuator
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-gamepad-user-gesture>
|
||||
pub(crate) fn contains_user_gesture(update_type: GamepadUpdateType) -> bool {
|
||||
match update_type {
|
||||
GamepadUpdateType::Axis(_, value) => value.abs() > AXIS_TILT_THRESHOLD,
|
||||
GamepadUpdateType::Button(_, value) => value > BUTTON_PRESS_THRESHOLD,
|
||||
}
|
||||
}
|
71
components/script/dom/gamepad/gamepadbutton.rs
Normal file
71
components/script/dom/gamepad/gamepadbutton.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::GamepadButtonBinding::GamepadButtonMethods;
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::window::Window;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct GamepadButton {
|
||||
reflector_: Reflector,
|
||||
pressed: Cell<bool>,
|
||||
touched: Cell<bool>,
|
||||
value: Cell<f64>,
|
||||
}
|
||||
|
||||
impl GamepadButton {
|
||||
pub(crate) fn new_inherited(pressed: bool, touched: bool) -> GamepadButton {
|
||||
Self {
|
||||
reflector_: Reflector::new(),
|
||||
pressed: Cell::new(pressed),
|
||||
touched: Cell::new(touched),
|
||||
value: Cell::new(0.0),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
window: &Window,
|
||||
pressed: bool,
|
||||
touched: bool,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<GamepadButton> {
|
||||
reflect_dom_object(
|
||||
Box::new(GamepadButton::new_inherited(pressed, touched)),
|
||||
window,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadButtonMethods<crate::DomTypeHolder> for GamepadButton {
|
||||
// https://www.w3.org/TR/gamepad/#widl-GamepadButton-pressed
|
||||
fn Pressed(&self) -> bool {
|
||||
self.pressed.get()
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/gamepad/#widl-GamepadButton-touched
|
||||
fn Touched(&self) -> bool {
|
||||
self.touched.get()
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/gamepad/#widl-GamepadButton-value
|
||||
fn Value(&self) -> Finite<f64> {
|
||||
Finite::wrap(self.value.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadButton {
|
||||
pub(crate) fn update(&self, pressed: bool, touched: bool, value: f64) {
|
||||
self.pressed.set(pressed);
|
||||
self.touched.set(touched);
|
||||
self.value.set(value);
|
||||
}
|
||||
}
|
88
components/script/dom/gamepad/gamepadbuttonlist.rs
Normal file
88
components/script/dom/gamepad/gamepadbuttonlist.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods;
|
||||
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, DomSlice};
|
||||
use crate::dom::gamepad::gamepadbutton::GamepadButton;
|
||||
use crate::dom::window::Window;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
// https://w3c.github.io/gamepad/#gamepadbutton-interface
|
||||
#[dom_struct]
|
||||
pub(crate) struct GamepadButtonList {
|
||||
reflector_: Reflector,
|
||||
list: Vec<Dom<GamepadButton>>,
|
||||
}
|
||||
|
||||
impl GamepadButtonList {
|
||||
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||
fn new_inherited(list: &[&GamepadButton]) -> GamepadButtonList {
|
||||
GamepadButtonList {
|
||||
reflector_: Reflector::new(),
|
||||
list: list.iter().map(|button| Dom::from_ref(*button)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
window: &Window,
|
||||
list: &[&GamepadButton],
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<GamepadButtonList> {
|
||||
reflect_dom_object(
|
||||
Box::new(GamepadButtonList::new_inherited(list)),
|
||||
window,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadButtonListMethods<crate::DomTypeHolder> for GamepadButtonList {
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-buttons
|
||||
fn Length(&self) -> u32 {
|
||||
self.list.len() as u32
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-buttons
|
||||
fn Item(&self, index: u32) -> Option<DomRoot<GamepadButton>> {
|
||||
self.list
|
||||
.get(index as usize)
|
||||
.map(|button| DomRoot::from_ref(&**button))
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#dom-gamepad-buttons
|
||||
fn IndexedGetter(&self, index: u32) -> Option<DomRoot<GamepadButton>> {
|
||||
self.Item(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadButtonList {
|
||||
/// Initialize the number of buttons in the "standard" gamepad mapping.
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-initializing-buttons>
|
||||
pub(crate) fn init_buttons(window: &Window, can_gc: CanGc) -> DomRoot<GamepadButtonList> {
|
||||
let standard_buttons = &[
|
||||
GamepadButton::new(window, false, false, can_gc), // Bottom button in right cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Right button in right cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Left button in right cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Top button in right cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Top left front button
|
||||
GamepadButton::new(window, false, false, can_gc), // Top right front button
|
||||
GamepadButton::new(window, false, false, can_gc), // Bottom left front button
|
||||
GamepadButton::new(window, false, false, can_gc), // Bottom right front button
|
||||
GamepadButton::new(window, false, false, can_gc), // Left button in center cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Right button in center cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Left stick pressed button
|
||||
GamepadButton::new(window, false, false, can_gc), // Right stick pressed button
|
||||
GamepadButton::new(window, false, false, can_gc), // Top button in left cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Bottom button in left cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Left button in left cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Right button in left cluster
|
||||
GamepadButton::new(window, false, false, can_gc), // Center button in center cluster
|
||||
];
|
||||
rooted_vec!(let buttons <- standard_buttons.iter().map(DomRoot::as_traced));
|
||||
Self::new(window, buttons.r(), can_gc)
|
||||
}
|
||||
}
|
118
components/script/dom/gamepad/gamepadevent.rs
Normal file
118
components/script/dom/gamepad/gamepadevent.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use js::rust::HandleObject;
|
||||
use stylo_atoms::Atom;
|
||||
|
||||
use super::gamepad::Gamepad;
|
||||
use crate::dom::bindings::codegen::Bindings::EventBinding::Event_Binding::EventMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::GamepadEventBinding;
|
||||
use crate::dom::bindings::codegen::Bindings::GamepadEventBinding::GamepadEventMethods;
|
||||
use crate::dom::bindings::error::Fallible;
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::window::Window;
|
||||
use crate::script_runtime::CanGc;
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct GamepadEvent {
|
||||
event: Event,
|
||||
gamepad: Dom<Gamepad>,
|
||||
}
|
||||
|
||||
pub(crate) enum GamepadEventType {
|
||||
Connected,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
impl GamepadEvent {
|
||||
fn new_inherited(gamepad: &Gamepad) -> GamepadEvent {
|
||||
GamepadEvent {
|
||||
event: Event::new_inherited(),
|
||||
gamepad: Dom::from_ref(gamepad),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
window: &Window,
|
||||
type_: Atom,
|
||||
bubbles: bool,
|
||||
cancelable: bool,
|
||||
gamepad: &Gamepad,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<GamepadEvent> {
|
||||
Self::new_with_proto(window, None, type_, bubbles, cancelable, gamepad, can_gc)
|
||||
}
|
||||
|
||||
fn new_with_proto(
|
||||
window: &Window,
|
||||
proto: Option<HandleObject>,
|
||||
type_: Atom,
|
||||
bubbles: bool,
|
||||
cancelable: bool,
|
||||
gamepad: &Gamepad,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<GamepadEvent> {
|
||||
let ev = reflect_dom_object_with_proto(
|
||||
Box::new(GamepadEvent::new_inherited(gamepad)),
|
||||
window,
|
||||
proto,
|
||||
can_gc,
|
||||
);
|
||||
{
|
||||
let event = ev.upcast::<Event>();
|
||||
event.init_event(type_, bubbles, cancelable);
|
||||
}
|
||||
ev
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_type(
|
||||
window: &Window,
|
||||
event_type: GamepadEventType,
|
||||
gamepad: &Gamepad,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<GamepadEvent> {
|
||||
let name = match event_type {
|
||||
GamepadEventType::Connected => "gamepadconnected",
|
||||
GamepadEventType::Disconnected => "gamepaddisconnected",
|
||||
};
|
||||
|
||||
GamepadEvent::new(window, name.into(), false, false, gamepad, can_gc)
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadEventMethods<crate::DomTypeHolder> for GamepadEvent {
|
||||
// https://w3c.github.io/gamepad/#gamepadevent-interface
|
||||
fn Constructor(
|
||||
window: &Window,
|
||||
proto: Option<HandleObject>,
|
||||
can_gc: CanGc,
|
||||
type_: DOMString,
|
||||
init: &GamepadEventBinding::GamepadEventInit,
|
||||
) -> Fallible<DomRoot<GamepadEvent>> {
|
||||
Ok(GamepadEvent::new_with_proto(
|
||||
window,
|
||||
proto,
|
||||
Atom::from(type_),
|
||||
init.parent.bubbles,
|
||||
init.parent.cancelable,
|
||||
&init.gamepad,
|
||||
can_gc,
|
||||
))
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/#gamepadevent-interface
|
||||
fn Gamepad(&self) -> DomRoot<Gamepad> {
|
||||
DomRoot::from_ref(&*self.gamepad)
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-event-istrusted
|
||||
fn IsTrusted(&self) -> bool {
|
||||
self.event.IsTrusted()
|
||||
}
|
||||
}
|
383
components/script/dom/gamepad/gamepadhapticactuator.rs
Normal file
383
components/script/dom/gamepad/gamepadhapticactuator.rs
Normal file
|
@ -0,0 +1,383 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use embedder_traits::{DualRumbleEffectParams, EmbedderMsg, GamepadSupportedHapticEffects};
|
||||
use ipc_channel::ipc;
|
||||
use ipc_channel::router::ROUTER;
|
||||
use js::rust::MutableHandleValue;
|
||||
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::GamepadHapticActuatorBinding::{
|
||||
GamepadEffectParameters, GamepadHapticActuatorMethods, GamepadHapticEffectType,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
|
||||
use crate::dom::bindings::error::Error;
|
||||
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
|
||||
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::utils::to_frozen_array;
|
||||
use crate::dom::promise::Promise;
|
||||
use crate::dom::window::Window;
|
||||
use crate::realms::InRealm;
|
||||
use crate::script_runtime::{CanGc, JSContext};
|
||||
use crate::task_source::SendableTaskSource;
|
||||
|
||||
struct HapticEffectListener {
|
||||
task_source: SendableTaskSource,
|
||||
context: Trusted<GamepadHapticActuator>,
|
||||
}
|
||||
|
||||
impl HapticEffectListener {
|
||||
fn handle_stopped(&self, stopped_successfully: bool) {
|
||||
let context = self.context.clone();
|
||||
self.task_source
|
||||
.queue(task!(handle_haptic_effect_stopped: move || {
|
||||
let actuator = context.root();
|
||||
actuator.handle_haptic_effect_stopped(stopped_successfully);
|
||||
}));
|
||||
}
|
||||
|
||||
fn handle_completed(&self, completed_successfully: bool) {
|
||||
let context = self.context.clone();
|
||||
self.task_source
|
||||
.queue(task!(handle_haptic_effect_completed: move || {
|
||||
let actuator = context.root();
|
||||
actuator.handle_haptic_effect_completed(completed_successfully, CanGc::note());
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#gamepadhapticactuator-interface>
|
||||
#[dom_struct]
|
||||
pub(crate) struct GamepadHapticActuator {
|
||||
reflector_: Reflector,
|
||||
gamepad_index: u32,
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-effects>
|
||||
effects: Vec<GamepadHapticEffectType>,
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-playingeffectpromise>
|
||||
#[ignore_malloc_size_of = "Rc is hard"]
|
||||
playing_effect_promise: DomRefCell<Option<Rc<Promise>>>,
|
||||
/// The current sequence ID for playing effects,
|
||||
/// incremented on every call to playEffect() or reset().
|
||||
/// Used to ensure that promises are resolved correctly.
|
||||
/// Based on this pending PR <https://github.com/w3c/gamepad/pull/201>
|
||||
sequence_id: Cell<u32>,
|
||||
/// The sequence ID during the last playEffect() call
|
||||
effect_sequence_id: Cell<u32>,
|
||||
/// The sequence ID during the last reset() call
|
||||
reset_sequence_id: Cell<u32>,
|
||||
}
|
||||
|
||||
impl GamepadHapticActuator {
|
||||
fn new_inherited(
|
||||
gamepad_index: u32,
|
||||
supported_haptic_effects: GamepadSupportedHapticEffects,
|
||||
) -> GamepadHapticActuator {
|
||||
let mut effects = vec![];
|
||||
if supported_haptic_effects.supports_dual_rumble {
|
||||
effects.push(GamepadHapticEffectType::Dual_rumble);
|
||||
}
|
||||
if supported_haptic_effects.supports_trigger_rumble {
|
||||
effects.push(GamepadHapticEffectType::Trigger_rumble);
|
||||
}
|
||||
Self {
|
||||
reflector_: Reflector::new(),
|
||||
gamepad_index,
|
||||
effects,
|
||||
playing_effect_promise: DomRefCell::new(None),
|
||||
sequence_id: Cell::new(0),
|
||||
effect_sequence_id: Cell::new(0),
|
||||
reset_sequence_id: Cell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
window: &Window,
|
||||
gamepad_index: u32,
|
||||
supported_haptic_effects: GamepadSupportedHapticEffects,
|
||||
can_gc: CanGc,
|
||||
) -> DomRoot<GamepadHapticActuator> {
|
||||
reflect_dom_object(
|
||||
Box::new(GamepadHapticActuator::new_inherited(
|
||||
gamepad_index,
|
||||
supported_haptic_effects,
|
||||
)),
|
||||
window,
|
||||
can_gc,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadHapticActuatorMethods<crate::DomTypeHolder> for GamepadHapticActuator {
|
||||
/// <https://www.w3.org/TR/gamepad/#dom-gamepadhapticactuator-effects>
|
||||
fn Effects(&self, cx: JSContext, can_gc: CanGc, retval: MutableHandleValue) {
|
||||
to_frozen_array(self.effects.as_slice(), cx, retval, can_gc)
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dom-gamepadhapticactuator-playeffect>
|
||||
fn PlayEffect(
|
||||
&self,
|
||||
type_: GamepadHapticEffectType,
|
||||
params: &GamepadEffectParameters,
|
||||
comp: InRealm,
|
||||
can_gc: CanGc,
|
||||
) -> Rc<Promise> {
|
||||
let playing_effect_promise = Promise::new_in_current_realm(comp, can_gc);
|
||||
|
||||
// <https://www.w3.org/TR/gamepad/#dfn-valid-effect>
|
||||
match type_ {
|
||||
// <https://www.w3.org/TR/gamepad/#dfn-valid-dual-rumble-effect>
|
||||
GamepadHapticEffectType::Dual_rumble => {
|
||||
if *params.strongMagnitude < 0.0 || *params.strongMagnitude > 1.0 {
|
||||
playing_effect_promise.reject_error(
|
||||
Error::Type(
|
||||
"Strong magnitude value is not within range of 0.0 to 1.0.".to_string(),
|
||||
),
|
||||
can_gc,
|
||||
);
|
||||
return playing_effect_promise;
|
||||
} else if *params.weakMagnitude < 0.0 || *params.weakMagnitude > 1.0 {
|
||||
playing_effect_promise.reject_error(
|
||||
Error::Type(
|
||||
"Weak magnitude value is not within range of 0.0 to 1.0.".to_string(),
|
||||
),
|
||||
can_gc,
|
||||
);
|
||||
return playing_effect_promise;
|
||||
}
|
||||
},
|
||||
// <https://www.w3.org/TR/gamepad/#dfn-valid-trigger-rumble-effect>
|
||||
GamepadHapticEffectType::Trigger_rumble => {
|
||||
if *params.strongMagnitude < 0.0 || *params.strongMagnitude > 1.0 {
|
||||
playing_effect_promise.reject_error(
|
||||
Error::Type(
|
||||
"Strong magnitude value is not within range of 0.0 to 1.0.".to_string(),
|
||||
),
|
||||
can_gc,
|
||||
);
|
||||
return playing_effect_promise;
|
||||
} else if *params.weakMagnitude < 0.0 || *params.weakMagnitude > 1.0 {
|
||||
playing_effect_promise.reject_error(
|
||||
Error::Type(
|
||||
"Weak magnitude value is not within range of 0.0 to 1.0.".to_string(),
|
||||
),
|
||||
can_gc,
|
||||
);
|
||||
return playing_effect_promise;
|
||||
} else if *params.leftTrigger < 0.0 || *params.leftTrigger > 1.0 {
|
||||
playing_effect_promise.reject_error(
|
||||
Error::Type(
|
||||
"Left trigger value is not within range of 0.0 to 1.0.".to_string(),
|
||||
),
|
||||
can_gc,
|
||||
);
|
||||
return playing_effect_promise;
|
||||
} else if *params.rightTrigger < 0.0 || *params.rightTrigger > 1.0 {
|
||||
playing_effect_promise.reject_error(
|
||||
Error::Type(
|
||||
"Right trigger value is not within range of 0.0 to 1.0.".to_string(),
|
||||
),
|
||||
can_gc,
|
||||
);
|
||||
return playing_effect_promise;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
let document = self.global().as_window().Document();
|
||||
if !document.is_fully_active() {
|
||||
playing_effect_promise.reject_error(Error::InvalidState, can_gc);
|
||||
}
|
||||
|
||||
self.sequence_id.set(self.sequence_id.get().wrapping_add(1));
|
||||
|
||||
if let Some(promise) = self.playing_effect_promise.borrow_mut().take() {
|
||||
let trusted_promise = TrustedPromise::new(promise);
|
||||
self.global().task_manager().gamepad_task_source().queue(
|
||||
task!(preempt_promise: move || {
|
||||
let promise = trusted_promise.root();
|
||||
let message = DOMString::from("preempted");
|
||||
promise.resolve_native(&message, CanGc::note());
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if !self.effects.contains(&type_) {
|
||||
playing_effect_promise.reject_error(Error::NotSupported, can_gc);
|
||||
return playing_effect_promise;
|
||||
}
|
||||
|
||||
*self.playing_effect_promise.borrow_mut() = Some(playing_effect_promise.clone());
|
||||
self.effect_sequence_id.set(self.sequence_id.get());
|
||||
|
||||
let context = Trusted::new(self);
|
||||
let (effect_complete_sender, effect_complete_receiver) =
|
||||
ipc::channel().expect("ipc channel failure");
|
||||
let listener = HapticEffectListener {
|
||||
task_source: self.global().task_manager().gamepad_task_source().into(),
|
||||
context,
|
||||
};
|
||||
|
||||
ROUTER.add_typed_route(
|
||||
effect_complete_receiver,
|
||||
Box::new(move |message| match message {
|
||||
Ok(msg) => listener.handle_completed(msg),
|
||||
Err(err) => warn!("Error receiving a GamepadMsg: {:?}", err),
|
||||
}),
|
||||
);
|
||||
|
||||
// Note: The spec says we SHOULD also pass a playEffectTimestamp for more precise playback timing
|
||||
// when start_delay is non-zero, but this is left more as a footnote without much elaboration.
|
||||
// <https://www.w3.org/TR/gamepad/#dfn-issue-a-haptic-effect>
|
||||
|
||||
let params = DualRumbleEffectParams {
|
||||
duration: params.duration as f64,
|
||||
start_delay: params.startDelay as f64,
|
||||
strong_magnitude: *params.strongMagnitude,
|
||||
weak_magnitude: *params.weakMagnitude,
|
||||
};
|
||||
let event = EmbedderMsg::PlayGamepadHapticEffect(
|
||||
document.webview_id(),
|
||||
self.gamepad_index as usize,
|
||||
embedder_traits::GamepadHapticEffectType::DualRumble(params),
|
||||
effect_complete_sender,
|
||||
);
|
||||
self.global().as_window().send_to_embedder(event);
|
||||
|
||||
playing_effect_promise
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dom-gamepadhapticactuator-reset>
|
||||
fn Reset(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
|
||||
let promise = Promise::new_in_current_realm(comp, can_gc);
|
||||
|
||||
let document = self.global().as_window().Document();
|
||||
if !document.is_fully_active() {
|
||||
promise.reject_error(Error::InvalidState, can_gc);
|
||||
return promise;
|
||||
}
|
||||
|
||||
self.sequence_id.set(self.sequence_id.get().wrapping_add(1));
|
||||
|
||||
if let Some(promise) = self.playing_effect_promise.borrow_mut().take() {
|
||||
let trusted_promise = TrustedPromise::new(promise);
|
||||
self.global().task_manager().gamepad_task_source().queue(
|
||||
task!(preempt_promise: move || {
|
||||
let promise = trusted_promise.root();
|
||||
let message = DOMString::from("preempted");
|
||||
promise.resolve_native(&message, CanGc::note());
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
*self.playing_effect_promise.borrow_mut() = Some(promise.clone());
|
||||
|
||||
self.reset_sequence_id.set(self.sequence_id.get());
|
||||
|
||||
let context = Trusted::new(self);
|
||||
let (effect_stop_sender, effect_stop_receiver) =
|
||||
ipc::channel().expect("ipc channel failure");
|
||||
let listener = HapticEffectListener {
|
||||
task_source: self.global().task_manager().gamepad_task_source().into(),
|
||||
context,
|
||||
};
|
||||
|
||||
ROUTER.add_typed_route(
|
||||
effect_stop_receiver,
|
||||
Box::new(move |message| match message {
|
||||
Ok(msg) => listener.handle_stopped(msg),
|
||||
Err(err) => warn!("Error receiving a GamepadMsg: {:?}", err),
|
||||
}),
|
||||
);
|
||||
|
||||
let event = EmbedderMsg::StopGamepadHapticEffect(
|
||||
document.webview_id(),
|
||||
self.gamepad_index as usize,
|
||||
effect_stop_sender,
|
||||
);
|
||||
self.global().as_window().send_to_embedder(event);
|
||||
|
||||
self.playing_effect_promise.borrow().clone().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadHapticActuator {
|
||||
/// <https://www.w3.org/TR/gamepad/#dom-gamepadhapticactuator-playeffect>
|
||||
/// We are in the task queued by the "in-parallel" steps.
|
||||
pub(crate) fn handle_haptic_effect_completed(
|
||||
&self,
|
||||
completed_successfully: bool,
|
||||
can_gc: CanGc,
|
||||
) {
|
||||
if self.effect_sequence_id.get() != self.sequence_id.get() || !completed_successfully {
|
||||
return;
|
||||
}
|
||||
let playing_effect_promise = self.playing_effect_promise.borrow_mut().take();
|
||||
if let Some(promise) = playing_effect_promise {
|
||||
let message = DOMString::from("complete");
|
||||
promise.resolve_native(&message, can_gc);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dom-gamepadhapticactuator-reset>
|
||||
/// We are in the task queued by the "in-parallel" steps.
|
||||
pub(crate) fn handle_haptic_effect_stopped(&self, stopped_successfully: bool) {
|
||||
if !stopped_successfully {
|
||||
return;
|
||||
}
|
||||
|
||||
let playing_effect_promise = self.playing_effect_promise.borrow_mut().take();
|
||||
|
||||
if let Some(promise) = playing_effect_promise {
|
||||
let trusted_promise = TrustedPromise::new(promise);
|
||||
let sequence_id = self.sequence_id.get();
|
||||
let reset_sequence_id = self.reset_sequence_id.get();
|
||||
self.global().task_manager().gamepad_task_source().queue(
|
||||
task!(complete_promise: move || {
|
||||
if sequence_id != reset_sequence_id {
|
||||
warn!("Mismatched sequence/reset sequence ids: {} != {}", sequence_id, reset_sequence_id);
|
||||
return;
|
||||
}
|
||||
let promise = trusted_promise.root();
|
||||
let message = DOMString::from("complete");
|
||||
promise.resolve_native(&message, CanGc::note());
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#handling-visibility-change>
|
||||
pub(crate) fn handle_visibility_change(&self) {
|
||||
if self.playing_effect_promise.borrow().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let this = Trusted::new(self);
|
||||
self.global().task_manager().gamepad_task_source().queue(
|
||||
task!(stop_playing_effect: move || {
|
||||
let actuator = this.root();
|
||||
let Some(promise) = actuator.playing_effect_promise.borrow_mut().take() else {
|
||||
return;
|
||||
};
|
||||
let message = DOMString::from("preempted");
|
||||
promise.resolve_native(&message, CanGc::note());
|
||||
}),
|
||||
);
|
||||
|
||||
let (send, _rcv) = ipc::channel().expect("ipc channel failure");
|
||||
|
||||
let document = self.global().as_window().Document();
|
||||
let event = EmbedderMsg::StopGamepadHapticEffect(
|
||||
document.webview_id(),
|
||||
self.gamepad_index as usize,
|
||||
send,
|
||||
);
|
||||
self.global().as_window().send_to_embedder(event);
|
||||
}
|
||||
}
|
92
components/script/dom/gamepad/gamepadpose.rs
Normal file
92
components/script/dom/gamepad/gamepadpose.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom_struct::dom_struct;
|
||||
use js::typedarray::{Float32, Float32Array};
|
||||
|
||||
use crate::dom::bindings::buffer_source::HeapBufferSource;
|
||||
use crate::dom::bindings::codegen::Bindings::GamepadPoseBinding::GamepadPoseMethods;
|
||||
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::script_runtime::{CanGc, JSContext};
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct GamepadPose {
|
||||
reflector_: Reflector,
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
position: HeapBufferSource<Float32>,
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
orientation: HeapBufferSource<Float32>,
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
linear_vel: HeapBufferSource<Float32>,
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
angular_vel: HeapBufferSource<Float32>,
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
linear_acc: HeapBufferSource<Float32>,
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
angular_acc: HeapBufferSource<Float32>,
|
||||
}
|
||||
|
||||
// TODO: support gamepad discovery
|
||||
#[allow(dead_code)]
|
||||
impl GamepadPose {
|
||||
fn new_inherited() -> GamepadPose {
|
||||
GamepadPose {
|
||||
reflector_: Reflector::new(),
|
||||
position: HeapBufferSource::default(),
|
||||
orientation: HeapBufferSource::default(),
|
||||
linear_vel: HeapBufferSource::default(),
|
||||
angular_vel: HeapBufferSource::default(),
|
||||
linear_acc: HeapBufferSource::default(),
|
||||
angular_acc: HeapBufferSource::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<GamepadPose> {
|
||||
reflect_dom_object(Box::new(GamepadPose::new_inherited()), global, can_gc)
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadPoseMethods<crate::DomTypeHolder> for GamepadPose {
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepadpose-position
|
||||
fn GetPosition(&self, _cx: JSContext) -> Option<Float32Array> {
|
||||
self.position.typed_array_to_option()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepadpose-hasposition
|
||||
fn HasPosition(&self) -> bool {
|
||||
self.position.is_initialized()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepadpose-linearvelocity
|
||||
fn GetLinearVelocity(&self, _cx: JSContext) -> Option<Float32Array> {
|
||||
self.linear_vel.typed_array_to_option()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepadpose-linearacceleration
|
||||
fn GetLinearAcceleration(&self, _cx: JSContext) -> Option<Float32Array> {
|
||||
self.linear_acc.typed_array_to_option()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepadpose-orientation
|
||||
fn GetOrientation(&self, _cx: JSContext) -> Option<Float32Array> {
|
||||
self.orientation.typed_array_to_option()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepadpose-orientation
|
||||
fn HasOrientation(&self) -> bool {
|
||||
self.orientation.is_initialized()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepadpose-angularvelocity
|
||||
fn GetAngularVelocity(&self, _cx: JSContext) -> Option<Float32Array> {
|
||||
self.angular_vel.typed_array_to_option()
|
||||
}
|
||||
|
||||
// https://w3c.github.io/gamepad/extensions.html#dom-gamepadpose-angularacceleration
|
||||
fn GetAngularAcceleration(&self, _cx: JSContext) -> Option<Float32Array> {
|
||||
self.angular_acc.typed_array_to_option()
|
||||
}
|
||||
}
|
12
components/script/dom/gamepad/mod.rs
Normal file
12
components/script/dom/gamepad/mod.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[expect(clippy::module_inception, reason = "The interface name is Gamepad")]
|
||||
pub(crate) mod gamepad;
|
||||
pub(crate) use gamepad::Gamepad;
|
||||
pub(crate) mod gamepadbutton;
|
||||
pub(crate) mod gamepadbuttonlist;
|
||||
pub(crate) mod gamepadevent;
|
||||
pub(crate) mod gamepadhapticactuator;
|
||||
pub(crate) mod gamepadpose;
|
Loading…
Add table
Add a link
Reference in a new issue