From f0de46dc95b03ec603583e3f6656ca6c177dc25d Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Tue, 5 Mar 2024 22:15:13 -1000 Subject: [PATCH 01/15] Implement Servo side of GamepadHapticActuator --- components/script/dom/gamepad.rs | 10 ++ .../script/dom/gamepadhapticactuator.rs | 169 ++++++++++++++++++ components/script/dom/mod.rs | 1 + components/script/dom/webidls/Gamepad.webidl | 1 + .../dom/webidls/GamepadHapticActuator.webidl | 33 ++++ 5 files changed, 214 insertions(+) create mode 100644 components/script/dom/gamepadhapticactuator.rs create mode 100644 components/script/dom/webidls/GamepadHapticActuator.webidl diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs index cee1077d00fc..dd434165909c 100644 --- a/components/script/dom/gamepad.rs +++ b/components/script/dom/gamepad.rs @@ -20,6 +20,7 @@ use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::gamepadbuttonlist::GamepadButtonList; use crate::dom::gamepadevent::{GamepadEvent, GamepadEventType}; +use crate::dom::gamepadhapticactuator::GamepadHapticActuator; use crate::dom::gamepadpose::GamepadPose; use crate::dom::globalscope::GlobalScope; use crate::script_runtime::JSContext; @@ -49,6 +50,7 @@ pub struct Gamepad { axis_bounds: (f64, f64), button_bounds: (f64, f64), exposed: Cell, + vibration_actuator: Dom, } impl Gamepad { @@ -65,6 +67,7 @@ impl Gamepad { hand: GamepadHand, axis_bounds: (f64, f64), button_bounds: (f64, f64), + vibration_actuator: &GamepadHapticActuator, ) -> Gamepad { Self { reflector_: Reflector::new(), @@ -81,6 +84,7 @@ impl Gamepad { axis_bounds, button_bounds, exposed: Cell::new(false), + vibration_actuator: Dom::from_ref(vibration_actuator), } } @@ -107,6 +111,7 @@ impl Gamepad { button_bounds: (f64, f64), ) -> DomRoot { let button_list = GamepadButtonList::init_buttons(global); + let vibration_actuator = GamepadHapticActuator::new(global); let gamepad = reflect_dom_object_with_proto( Box::new(Gamepad::new_inherited( gamepad_id, @@ -120,6 +125,7 @@ impl Gamepad { GamepadHand::_empty, axis_bounds, button_bounds, + &vibration_actuator, )), global, None, @@ -165,6 +171,10 @@ impl GamepadMethods for Gamepad { DomRoot::from_ref(&*self.buttons) } + fn VibrationActuator(&self) -> DomRoot { + DomRoot::from_ref(&*self.vibration_actuator) + } + // https://w3c.github.io/gamepad/extensions.html#gamepadhand-enum fn Hand(&self) -> GamepadHand { self.hand diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs new file mode 100644 index 000000000000..e34146f580e4 --- /dev/null +++ b/components/script/dom/gamepadhapticactuator.rs @@ -0,0 +1,169 @@ +/* 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::rc::Rc; + +use dom_struct::dom_struct; +use js::jsval::JSVal; + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::GamepadHapticActuatorBinding::{ + GamepadEffectParameters, GamepadHapticActuatorMethods, GamepadHapticEffectType +}; +use crate::dom::bindings::codegen::Bindings::PerformanceBinding::Performance_Binding::PerformanceMethods; +use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::refcounted::TrustedPromise; +use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::utils::to_frozen_array; +use crate::dom::globalscope::GlobalScope; +use crate::dom::promise::Promise; +use crate::script_runtime::JSContext; +use crate::task_source::TaskSource; + +#[dom_struct] +pub struct GamepadHapticActuator { + reflector_: Reflector, + effects: Vec, + #[ignore_malloc_size_of = "promises are hard"] + playing_effect_promise: DomRefCell>>, +} + +impl GamepadHapticActuator { + fn new_inherited() -> GamepadHapticActuator { + Self { + reflector_: Reflector::new(), + effects: vec!(GamepadHapticEffectType::Dual_rumble), + playing_effect_promise: DomRefCell::new(None), + } + } + + pub fn new(global: &GlobalScope) -> DomRoot { + Self::new_with_proto(global) + } + + fn new_with_proto(global: &GlobalScope) -> DomRoot { + let haptic_actuator = reflect_dom_object_with_proto( + Box::new(GamepadHapticActuator::new_inherited()), + global, + None, + ); + haptic_actuator + } +} + +impl GamepadHapticActuatorMethods for GamepadHapticActuator { + /// + fn Effects(&self, cx: JSContext) -> JSVal { + to_frozen_array(self.effects.as_slice(), cx) + } + + /// + fn PlayEffect(&self, type_: GamepadHapticEffectType, params: &GamepadEffectParameters) -> Rc { + let playing_effect_promise = Promise::new(&self.global()); + + // + match type_ { + // + 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())); + } 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())); + } + } + } + + let document = self.global().as_window().Document(); + if !document.is_fully_active() { + playing_effect_promise.reject_error(Error::InvalidState); + } + + if self.playing_effect_promise.borrow().is_some() { + let trusted_promise = TrustedPromise::new( + self.playing_effect_promise + .borrow() + .clone() + .expect("Promise is null!") + ); + *self.playing_effect_promise.borrow_mut() = None; + let _ = self.global().gamepad_task_source().queue( + task!(preempt_promise: move || { + let promise = trusted_promise.root(); + let message = DOMString::from("preempted"); + promise.resolve_native(&message); + }), + &self.global() + ); + } + + if !self.effects.contains(&type_) { + playing_effect_promise.reject_error(Error::NotSupported); + } + + *self.playing_effect_promise.borrow_mut() = Some(playing_effect_promise.clone()); + let play_effect_timestamp = self.global().performance().Now(); + + // TODO: Play haptic effect + + playing_effect_promise.resolve_native(&()); + + playing_effect_promise + } + + /// + fn Reset(&self) -> Rc { + let reset_result_promise = Promise::new(&self.global()); + + let document = self.global().as_window().Document(); + if !document.is_fully_active() { + reset_result_promise.reject_error(Error::InvalidState); + } + + if self.playing_effect_promise.borrow().is_some() { + // TODO: Stop existing haptic effect + let completed_successfully = true; + if completed_successfully { + let message = DOMString::from("completed"); + reset_result_promise.resolve_native(&message); + } + } + + reset_result_promise + } +} + +impl GamepadHapticActuator { + /// + #[allow(dead_code)] + pub fn handle_visibility_change(&self) { + if self.playing_effect_promise.is_none() { + return; + } + + let trusted_promise = TrustedPromise::new( + self.playing_effect_promise + .borrow() + .clone() + .expect("Promise is null!") + ); + + let this = Trusted::new(&*self); + + let _ = self.global().gamepad_task_source().queue( + task!(stop_playing_effect: move || { + let promise = trusted_promise.root(); + let actuator = this.root(); + let message = DOMString::from("preempted"); + promise.resolve_native(&message); + actuator.playing_effect_promise.borrow_mut() = None; + }), + &self.global(), + ); + + // TODO: Stop haptic effect + } +} \ No newline at end of file diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 590a90f93702..703f6a2a8fc7 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -319,6 +319,7 @@ pub mod gamepad; pub mod gamepadbutton; pub mod gamepadbuttonlist; pub mod gamepadevent; +pub mod gamepadhapticactuator; pub mod gamepadpose; pub mod globalscope; pub mod gpu; diff --git a/components/script/dom/webidls/Gamepad.webidl b/components/script/dom/webidls/Gamepad.webidl index 306aa0c216b3..d716f0c0bc4c 100644 --- a/components/script/dom/webidls/Gamepad.webidl +++ b/components/script/dom/webidls/Gamepad.webidl @@ -12,6 +12,7 @@ interface Gamepad { readonly attribute DOMString mapping; readonly attribute Float64Array axes; [SameObject] readonly attribute GamepadButtonList buttons; + [SameObject] readonly attribute GamepadHapticActuator vibrationActuator; }; // https://w3c.github.io/gamepad/extensions.html#partial-gamepad-interface diff --git a/components/script/dom/webidls/GamepadHapticActuator.webidl b/components/script/dom/webidls/GamepadHapticActuator.webidl new file mode 100644 index 000000000000..8af63291aa70 --- /dev/null +++ b/components/script/dom/webidls/GamepadHapticActuator.webidl @@ -0,0 +1,33 @@ +/* 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/. */ + +// https://www.w3.org/TR/gamepad/#gamepadhapticactuator-interface +[Exposed=Window, Pref="dom.gamepad.enabled"] +interface GamepadHapticActuator { + /* [SameObject] */ readonly attribute /* FrozenArray */ any effects; + Promise playEffect( + GamepadHapticEffectType type, + optional GamepadEffectParameters params = {} + ); + Promise reset(); +}; + +// https://www.w3.org/TR/gamepad/#gamepadhapticsresult-enum +enum GamepadHapticsResult { + "complete", + "preempted" +}; + +// https://www.w3.org/TR/gamepad/#dom-gamepadhapticeffecttype +enum GamepadHapticEffectType { + "dual-rumble" +}; + +// https://www.w3.org/TR/gamepad/#dom-gamepadeffectparameters +dictionary GamepadEffectParameters { + unsigned long long duration = 0; + unsigned long long startDelay = 0; + double strongMagnitude = 0.0; + double weakMagnitude = 0.0; +}; From f00e55e4c2a744b6c159f8bf47fc678a0ecbd6ac Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Thu, 7 Mar 2024 08:41:28 -1000 Subject: [PATCH 02/15] Get build working --- components/script/dom/gamepadhapticactuator.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index e34146f580e4..fad9631c2de4 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -14,7 +14,7 @@ use crate::dom::bindings::codegen::Bindings::GamepadHapticActuatorBinding::{ use crate::dom::bindings::codegen::Bindings::PerformanceBinding::Performance_Binding::PerformanceMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; use crate::dom::bindings::error::Error; -use crate::dom::bindings::refcounted::TrustedPromise; +use crate::dom::bindings::refcounted::{Trusted, TrustedPromise}; use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; @@ -140,7 +140,7 @@ impl GamepadHapticActuator { /// #[allow(dead_code)] pub fn handle_visibility_change(&self) { - if self.playing_effect_promise.is_none() { + if self.playing_effect_promise.borrow().is_none() { return; } @@ -159,7 +159,7 @@ impl GamepadHapticActuator { let actuator = this.root(); let message = DOMString::from("preempted"); promise.resolve_native(&message); - actuator.playing_effect_promise.borrow_mut() = None; + *actuator.playing_effect_promise.borrow_mut() = None; }), &self.global(), ); From d2488598587a84587785486160f70916f3ae26b8 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Sat, 9 Mar 2024 19:49:35 -1000 Subject: [PATCH 03/15] Create effect handling on embedder side --- components/script/dom/gamepad.rs | 4 ++ .../script/dom/gamepadhapticactuator.rs | 46 +++++++++++------ components/script/dom/globalscope.rs | 23 +++++++++ components/shared/embedder/lib.rs | 16 ++++++ components/shared/script/lib.rs | 2 + ports/servoshell/webview.rs | 49 ++++++++++++++++++- 6 files changed, 124 insertions(+), 16 deletions(-) diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs index dd434165909c..66539bebf34c 100644 --- a/components/script/dom/gamepad.rs +++ b/components/script/dom/gamepad.rs @@ -296,6 +296,10 @@ impl Gamepad { pub fn set_exposed(&self, exposed: bool) { self.exposed.set(exposed); } + + pub fn vibration_actuator(&self) -> &GamepadHapticActuator { + &*self.vibration_actuator + } } /// diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index fad9631c2de4..50e37226d306 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -9,7 +9,7 @@ use js::jsval::JSVal; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::GamepadHapticActuatorBinding::{ - GamepadEffectParameters, GamepadHapticActuatorMethods, GamepadHapticEffectType + GamepadEffectParameters, GamepadHapticActuatorMethods, GamepadHapticEffectType, }; use crate::dom::bindings::codegen::Bindings::PerformanceBinding::Performance_Binding::PerformanceMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; @@ -36,7 +36,7 @@ impl GamepadHapticActuator { fn new_inherited() -> GamepadHapticActuator { Self { reflector_: Reflector::new(), - effects: vec!(GamepadHapticEffectType::Dual_rumble), + effects: vec![GamepadHapticEffectType::Dual_rumble], playing_effect_promise: DomRefCell::new(None), } } @@ -62,7 +62,11 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { } /// - fn PlayEffect(&self, type_: GamepadHapticEffectType, params: &GamepadEffectParameters) -> Rc { + fn PlayEffect( + &self, + type_: GamepadHapticEffectType, + params: &GamepadEffectParameters, + ) -> Rc { let playing_effect_promise = Promise::new(&self.global()); // @@ -70,11 +74,15 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { // 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())); + playing_effect_promise.reject_error(Error::Type( + "Strong magnitude value is not within range of 0.0 to 1.0.".to_string(), + )); } 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())); + playing_effect_promise.reject_error(Error::Type( + "Weak magnitude value is not within range of 0.0 to 1.0.".to_string(), + )); } - } + }, } let document = self.global().as_window().Document(); @@ -87,8 +95,8 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { self.playing_effect_promise .borrow() .clone() - .expect("Promise is null!") - ); + .expect("Promise is null!"), + ); *self.playing_effect_promise.borrow_mut() = None; let _ = self.global().gamepad_task_source().queue( task!(preempt_promise: move || { @@ -96,7 +104,7 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { let message = DOMString::from("preempted"); promise.resolve_native(&message); }), - &self.global() + &self.global(), ); } @@ -109,8 +117,6 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { // TODO: Play haptic effect - playing_effect_promise.resolve_native(&()); - playing_effect_promise } @@ -137,6 +143,18 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { } impl GamepadHapticActuator { + pub fn has_playing_effect_promise(&self) -> bool { + self.playing_effect_promise.borrow().is_some() + } + + pub fn resolve_playing_effect_promise(&self) { + let playing_effect_promise = self.playing_effect_promise.borrow().clone(); + if let Some(promise) = playing_effect_promise { + let message = DOMString::from("completed"); + promise.resolve_native(&message); + } + } + /// #[allow(dead_code)] pub fn handle_visibility_change(&self) { @@ -148,8 +166,8 @@ impl GamepadHapticActuator { self.playing_effect_promise .borrow() .clone() - .expect("Promise is null!") - ); + .expect("Promise is null!"), + ); let this = Trusted::new(&*self); @@ -166,4 +184,4 @@ impl GamepadHapticActuator { // TODO: Stop haptic effect } -} \ No newline at end of file +} diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 11edae2a21de..183cfa6a4aac 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -3127,6 +3127,9 @@ impl GlobalScope { GamepadEvent::Updated(index, update_type) => { self.receive_new_gamepad_button_or_axis(index.0, update_type); }, + GamepadEvent::HapticEffectCompleted(index) => { + self.handle_gamepad_haptic_effect_completed(index.0); + }, }; } @@ -3234,6 +3237,26 @@ impl GlobalScope { .expect("Failed to queue update gamepad state task."); } + pub fn handle_gamepad_haptic_effect_completed(&self, index: usize) { + let this = Trusted::new(&*self); + self.gamepad_task_source() + .queue_with_canceller( + task!(gamepad_haptic_effect_completed: move || { + let global = this.root(); + if let Some(window) = global.downcast::() { + let gamepad_list = window.Navigator().gamepads(); + if let Some(gamepad) = gamepad_list.Item(index as u32) { + if gamepad.vibration_actuator().has_playing_effect_promise() { + gamepad.vibration_actuator().resolve_playing_effect_promise(); + } + } + } + }), + &self.task_canceller(TaskSourceName::Gamepad), + ) + .expect("Failed to queue gamepad haptic effect completed task."); + } + pub(crate) fn current_group_label(&self) -> Option { self.console_group_stack .borrow() diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs index 0acf7d01ce5a..44973ff3f50a 100644 --- a/components/shared/embedder/lib.rs +++ b/components/shared/embedder/lib.rs @@ -214,6 +214,8 @@ pub enum EmbedderMsg { ReadyToPresent(Vec), /// The given event was delivered to a pipeline in the given browser. EventDelivered(CompositorEventVariant), + /// Request to play a haptic effect on a connected gamepad. + GamepadHapticEffect(usize, GamepadHapticEffectType), } /// The variant of CompositorEvent that was delivered to a pipeline. @@ -268,6 +270,7 @@ impl Debug for EmbedderMsg { EmbedderMsg::ShowContextMenu(..) => write!(f, "ShowContextMenu"), EmbedderMsg::ReadyToPresent(..) => write!(f, "ReadyToPresent"), EmbedderMsg::EventDelivered(..) => write!(f, "HitTestedEvent"), + EmbedderMsg::GamepadHapticEffect(..) => write!(f, "GamepadHapticEffect"), } } } @@ -368,3 +371,16 @@ pub enum PermissionRequest { Granted, Denied, } + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct DualRumbleEffectParams { + pub duration: f64, + pub start_delay: f64, + pub strong_magnitude: f64, + pub weak_magnitude: f64, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum GamepadHapticEffectType { + DualRumble(DualRumbleEffectParams), +} diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 76d0e74bd311..332c267ae074 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -1352,6 +1352,8 @@ pub enum GamepadEvent { /// An existing gamepad has been updated /// Updated(GamepadIndex, GamepadUpdateType), + /// A gamepad haptic effect has completed + HapticEffectCompleted(GamepadIndex), } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/ports/servoshell/webview.rs b/ports/servoshell/webview.rs index 34d87da6fe32..18ea205d701e 100644 --- a/ports/servoshell/webview.rs +++ b/ports/servoshell/webview.rs @@ -12,13 +12,15 @@ use std::{env, thread}; use arboard::Clipboard; use euclid::{Point2D, Vector2D}; +use gilrs::ff::{BaseEffect, BaseEffectType, EffectBuilder, Repeat, Replay, Ticks}; use gilrs::{EventType, Gilrs}; use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher}; use log::{debug, error, info, trace, warn}; use servo::compositing::windowing::{EmbedderEvent, WebRenderDebugOption}; use servo::embedder_traits::{ - CompositorEventVariant, ContextMenuResult, EmbedderMsg, FilterPattern, PermissionPrompt, - PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, + CompositorEventVariant, ContextMenuResult, DualRumbleEffectParams, EmbedderMsg, FilterPattern, + GamepadHapticEffectType, PermissionPrompt, PermissionRequest, PromptDefinition, PromptOrigin, + PromptResult, }; use servo::msg::constellation_msg::{TopLevelBrowsingContextId as WebViewId, TraversalDirection}; use servo::script_traits::{ @@ -250,6 +252,44 @@ where } } + fn play_haptic_effect(&mut self, index: usize, params: DualRumbleEffectParams) { + if let Some(ref mut gilrs) = self.gamepad { + if let Some(connected_gamepad) = gilrs + .gamepads() + .find(|gamepad| usize::from(gamepad.0) == index) + { + let gamepad = connected_gamepad.1; + let duration = Ticks::from_ms(params.duration as u32); + let scheduling = Replay { + after: Ticks::from_ms(params.start_delay as u32), + play_for: duration, + with_delay: Ticks::from_ms(0), + }; + let effect = EffectBuilder::new() + .add_effect(BaseEffect { + kind: BaseEffectType::Strong { magnitude: params.strong_magnitude as u16 }, + scheduling, + envelope: Default::default(), + }) + .add_effect(BaseEffect { + kind: BaseEffectType::Weak { magnitude: params.weak_magnitude as u16 }, + scheduling, + envelope: Default::default(), + }) + .repeat(Repeat::For(duration)) + .add_gamepad(&gamepad) + .finish(gilrs) + .expect("Failed to create haptic effect, gamepad is either disconnected or doesn't support force feedback."); + effect.play().expect("Failed to play haptic effect."); + + // Until we have a way to poll for effect completion or get an event for it, + // just send the completed event immediately. + let event = GamepadEvent::HapticEffectCompleted(GamepadIndex(index)); + self.event_queue.push(EmbedderEvent::Gamepad(event)); + } + } + } + pub fn shutdown_requested(&self) -> bool { self.shutdown_requested } @@ -735,6 +775,11 @@ where .push(EmbedderEvent::FocusWebView(webview_id)); } }, + EmbedderMsg::GamepadHapticEffect(index, effect) => match effect { + GamepadHapticEffectType::DualRumble(params) => { + self.play_haptic_effect(index, params) + }, + }, } } From 834b781e44c26c69abe68d5a037c7d61a50c8dfc Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Tue, 9 Apr 2024 09:37:20 -1000 Subject: [PATCH 04/15] Update tracing for GamepadHapticEffect --- components/constellation/tracing.rs | 1 + ports/servoshell/tracing.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs index 040a8bd32696..fe799a47ec97 100644 --- a/components/constellation/tracing.rs +++ b/components/constellation/tracing.rs @@ -240,6 +240,7 @@ mod from_script { Self::OnDevtoolsStarted(..) => target_variant!("OnDevtoolsStarted"), Self::ReadyToPresent(..) => target_variant!("ReadyToPresent"), Self::EventDelivered(..) => target_variant!("EventDelivered"), + Self::GamepadHapticEffect(..) => target_variant!("GamepadHapticEffect"), } } } diff --git a/ports/servoshell/tracing.rs b/ports/servoshell/tracing.rs index 394765d14236..f0a026143a4b 100644 --- a/ports/servoshell/tracing.rs +++ b/ports/servoshell/tracing.rs @@ -174,6 +174,7 @@ mod from_servo { Self::OnDevtoolsStarted(..) => target!("OnDevtoolsStarted"), Self::ReadyToPresent(..) => target!("ReadyToPresent"), Self::EventDelivered(..) => target!("EventDelivered"), + Self::GamepadHapticEffect(..) => target!("GamepadHapticEffect"), } } } From 454324668c6818411481c2e2298c84a78fcc8a59 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Tue, 9 Apr 2024 09:37:51 -1000 Subject: [PATCH 05/15] Update gilrs to point to commit with effect complete event --- Cargo.lock | 6 ++---- ports/servoshell/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4216c66393b9..b8b2fe175892 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1994,8 +1994,7 @@ dependencies = [ [[package]] name = "gilrs" version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "499067aa54af19f88732dc418f61f23d5912de1518665bb0eca034ca0d07574c" +source = "git+https://gitlab.com/gilrs-project/gilrs#eba723a27a324a631cb65c62c9410a4f928d4da2" dependencies = [ "fnv", "gilrs-core", @@ -2007,8 +2006,7 @@ dependencies = [ [[package]] name = "gilrs-core" version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c132270a155f2548e67d66e731075c336c39098afc555752f3df8f882c720e" +source = "git+https://gitlab.com/gilrs-project/gilrs#eba723a27a324a631cb65c62c9410a4f928d4da2" dependencies = [ "core-foundation", "inotify", diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index 9e916dd68a99..2b5759a49d87 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -62,7 +62,7 @@ egui_glow = { version = "0.22.0", features = ["winit"] } egui-winit = { version = "0.22.0", default-features = false, features = ["clipboard", "wayland"] } euclid = { workspace = true } getopts = { workspace = true } -gilrs = "0.10.6" +gilrs = { git = "https://gitlab.com/gilrs-project/gilrs" } gleam = { workspace = true } glow = "0.12.2" keyboard-types = { workspace = true } From a5b12d2a75c8ae647ec6a5fc1185fc7de4293ad8 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Tue, 9 Apr 2024 09:40:30 -1000 Subject: [PATCH 06/15] Implement playing and preempting haptic effects --- components/script/dom/gamepad.rs | 2 +- .../script/dom/gamepadhapticactuator.rs | 22 +++++++++---- components/script/dom/globalscope.rs | 3 +- ports/servoshell/webview.rs | 32 +++++++++++-------- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs index 66539bebf34c..6521b0204b09 100644 --- a/components/script/dom/gamepad.rs +++ b/components/script/dom/gamepad.rs @@ -111,7 +111,7 @@ impl Gamepad { button_bounds: (f64, f64), ) -> DomRoot { let button_list = GamepadButtonList::init_buttons(global); - let vibration_actuator = GamepadHapticActuator::new(global); + let vibration_actuator = GamepadHapticActuator::new(global, gamepad_id); let gamepad = reflect_dom_object_with_proto( Box::new(Gamepad::new_inherited( gamepad_id, diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index 50e37226d306..7a6c1339d88f 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -5,6 +5,7 @@ use std::rc::Rc; use dom_struct::dom_struct; +use embedder_traits::{DualRumbleEffectParams, EmbedderMsg}; use js::jsval::JSVal; use crate::dom::bindings::cell::DomRefCell; @@ -27,27 +28,29 @@ use crate::task_source::TaskSource; #[dom_struct] pub struct GamepadHapticActuator { reflector_: Reflector, + gamepad_index: u32, effects: Vec, #[ignore_malloc_size_of = "promises are hard"] playing_effect_promise: DomRefCell>>, } impl GamepadHapticActuator { - fn new_inherited() -> GamepadHapticActuator { + fn new_inherited(gamepad_index: u32) -> GamepadHapticActuator { Self { reflector_: Reflector::new(), + gamepad_index: gamepad_index.into(), effects: vec![GamepadHapticEffectType::Dual_rumble], playing_effect_promise: DomRefCell::new(None), } } - pub fn new(global: &GlobalScope) -> DomRoot { - Self::new_with_proto(global) + pub fn new(global: &GlobalScope, gamepad_index: u32) -> DomRoot { + Self::new_with_proto(global, gamepad_index) } - fn new_with_proto(global: &GlobalScope) -> DomRoot { + fn new_with_proto(global: &GlobalScope, gamepad_index: u32) -> DomRoot { let haptic_actuator = reflect_dom_object_with_proto( - Box::new(GamepadHapticActuator::new_inherited()), + Box::new(GamepadHapticActuator::new_inherited(gamepad_index)), global, None, ); @@ -115,7 +118,14 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { *self.playing_effect_promise.borrow_mut() = Some(playing_effect_promise.clone()); let play_effect_timestamp = self.global().performance().Now(); - // TODO: Play 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::GamepadHapticEffect(self.gamepad_index as usize, embedder_traits::GamepadHapticEffectType::DualRumble(params)); + self.global().as_window().send_to_embedder(event); playing_effect_promise } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 183cfa6a4aac..d8cc1f821110 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -3244,8 +3244,7 @@ impl GlobalScope { task!(gamepad_haptic_effect_completed: move || { let global = this.root(); if let Some(window) = global.downcast::() { - let gamepad_list = window.Navigator().gamepads(); - if let Some(gamepad) = gamepad_list.Item(index as u32) { + if let Some(gamepad) = window.Navigator().get_gamepad(index) { if gamepad.vibration_actuator().has_playing_effect_promise() { gamepad.vibration_actuator().resolve_playing_effect_promise(); } diff --git a/ports/servoshell/webview.rs b/ports/servoshell/webview.rs index 18ea205d701e..fcd28e2fd330 100644 --- a/ports/servoshell/webview.rs +++ b/ports/servoshell/webview.rs @@ -12,7 +12,7 @@ use std::{env, thread}; use arboard::Clipboard; use euclid::{Point2D, Vector2D}; -use gilrs::ff::{BaseEffect, BaseEffectType, EffectBuilder, Repeat, Replay, Ticks}; +use gilrs::ff::{BaseEffect, BaseEffectType, Effect, EffectBuilder, Repeat, Replay, Ticks}; use gilrs::{EventType, Gilrs}; use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher}; use log::{debug, error, info, trace, warn}; @@ -58,6 +58,7 @@ pub struct WebViewManager { event_queue: Vec, clipboard: Option, gamepad: Option, + haptic_effects: Vec, shutdown_requested: bool, load_status: LoadStatus, } @@ -106,6 +107,7 @@ where None }, }, + haptic_effects: vec![], event_queue: Vec::new(), shutdown_requested: false, load_status: LoadStatus::LoadComplete, @@ -217,6 +219,9 @@ where EventType::Disconnected => { gamepad_event = Some(GamepadEvent::Disconnected(index)); }, + EventType::ForceFeedbackEffectCompleted => { + gamepad_event = Some(GamepadEvent::HapticEffectCompleted(index)); + } _ => {}, } @@ -258,34 +263,35 @@ where .gamepads() .find(|gamepad| usize::from(gamepad.0) == index) { - let gamepad = connected_gamepad.1; + let start_delay = Ticks::from_ms(params.start_delay as u32); let duration = Ticks::from_ms(params.duration as u32); + let strong_magnitude = (params.strong_magnitude * u16::MAX as f64).round() as u16; + let weak_magnitude = (params.weak_magnitude * u16::MAX as f64).round() as u16; + let scheduling = Replay { - after: Ticks::from_ms(params.start_delay as u32), + after: start_delay, play_for: duration, with_delay: Ticks::from_ms(0), }; let effect = EffectBuilder::new() .add_effect(BaseEffect { - kind: BaseEffectType::Strong { magnitude: params.strong_magnitude as u16 }, + kind: BaseEffectType::Strong { magnitude: strong_magnitude }, scheduling, envelope: Default::default(), }) .add_effect(BaseEffect { - kind: BaseEffectType::Weak { magnitude: params.weak_magnitude as u16 }, + kind: BaseEffectType::Weak { magnitude: weak_magnitude }, scheduling, envelope: Default::default(), }) - .repeat(Repeat::For(duration)) - .add_gamepad(&gamepad) + .repeat(Repeat::For(start_delay + duration)) + .add_gamepad(&connected_gamepad.1) .finish(gilrs) .expect("Failed to create haptic effect, gamepad is either disconnected or doesn't support force feedback."); - effect.play().expect("Failed to play haptic effect."); - - // Until we have a way to poll for effect completion or get an event for it, - // just send the completed event immediately. - let event = GamepadEvent::HapticEffectCompleted(GamepadIndex(index)); - self.event_queue.push(EmbedderEvent::Gamepad(event)); + self.haptic_effects.push(effect); + self.haptic_effects[0].play().expect("Failed to play haptic effect."); + } else { + println!("Couldn't find connected gamepad to play haptic effect on"); } } } From 0eea9132f407f68c54c7f7c99633fec7fa882617 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Tue, 9 Apr 2024 19:30:49 -1000 Subject: [PATCH 07/15] Update IDL to add trigger rumble --- components/script/dom/gamepadhapticactuator.rs | 12 ++++++++++++ .../script/dom/webidls/GamepadHapticActuator.webidl | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index 7a6c1339d88f..0339590b44bb 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -39,6 +39,7 @@ impl GamepadHapticActuator { Self { reflector_: Reflector::new(), gamepad_index: gamepad_index.into(), + // TODO: Determine support from gilrs instead of assuming effects: vec![GamepadHapticEffectType::Dual_rumble], playing_effect_promise: DomRefCell::new(None), } @@ -86,6 +87,17 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { )); } }, + 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(), + )); + } 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(), + )); + } + } } let document = self.global().as_window().Document(); diff --git a/components/script/dom/webidls/GamepadHapticActuator.webidl b/components/script/dom/webidls/GamepadHapticActuator.webidl index 8af63291aa70..8eb7fe1d2fb1 100644 --- a/components/script/dom/webidls/GamepadHapticActuator.webidl +++ b/components/script/dom/webidls/GamepadHapticActuator.webidl @@ -21,7 +21,8 @@ enum GamepadHapticsResult { // https://www.w3.org/TR/gamepad/#dom-gamepadhapticeffecttype enum GamepadHapticEffectType { - "dual-rumble" + "dual-rumble", + "trigger-rumble" }; // https://www.w3.org/TR/gamepad/#dom-gamepadeffectparameters @@ -30,4 +31,6 @@ dictionary GamepadEffectParameters { unsigned long long startDelay = 0; double strongMagnitude = 0.0; double weakMagnitude = 0.0; + double leftTrigger = 0.0; + double rightTrigger = 0.0; }; From faef600866cda3f3c9e949dbf116a57f349c64df Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Tue, 9 Apr 2024 19:30:57 -1000 Subject: [PATCH 08/15] Update WPT expectations --- .../gamepad/idlharness.window.js.ini | 30 ------------------- .../wpt/meta/gamepad/idlharness.window.js.ini | 30 ------------------- 2 files changed, 60 deletions(-) diff --git a/tests/wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini b/tests/wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini index 37c2bd87ea1f..885d39022dbf 100644 --- a/tests/wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini +++ b/tests/wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini @@ -1,34 +1,4 @@ [idlharness.window.html] - [Gamepad interface: attribute vibrationActuator] - expected: FAIL - - [GamepadHapticActuator interface: existence and properties of interface object] - expected: FAIL - - [GamepadHapticActuator interface object length] - expected: FAIL - - [GamepadHapticActuator interface object name] - expected: FAIL - - [GamepadHapticActuator interface: existence and properties of interface prototype object] - expected: FAIL - - [GamepadHapticActuator interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [GamepadHapticActuator interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [GamepadHapticActuator interface: attribute effects] - expected: FAIL - - [GamepadHapticActuator interface: operation playEffect(GamepadHapticEffectType, optional GamepadEffectParameters)] - expected: FAIL - - [GamepadHapticActuator interface: operation reset()] - expected: FAIL - [GamepadEvent must be primary interface of new GamepadEvent("gamepad")] expected: FAIL diff --git a/tests/wpt/meta/gamepad/idlharness.window.js.ini b/tests/wpt/meta/gamepad/idlharness.window.js.ini index 37c2bd87ea1f..885d39022dbf 100644 --- a/tests/wpt/meta/gamepad/idlharness.window.js.ini +++ b/tests/wpt/meta/gamepad/idlharness.window.js.ini @@ -1,34 +1,4 @@ [idlharness.window.html] - [Gamepad interface: attribute vibrationActuator] - expected: FAIL - - [GamepadHapticActuator interface: existence and properties of interface object] - expected: FAIL - - [GamepadHapticActuator interface object length] - expected: FAIL - - [GamepadHapticActuator interface object name] - expected: FAIL - - [GamepadHapticActuator interface: existence and properties of interface prototype object] - expected: FAIL - - [GamepadHapticActuator interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [GamepadHapticActuator interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [GamepadHapticActuator interface: attribute effects] - expected: FAIL - - [GamepadHapticActuator interface: operation playEffect(GamepadHapticEffectType, optional GamepadEffectParameters)] - expected: FAIL - - [GamepadHapticActuator interface: operation reset()] - expected: FAIL - [GamepadEvent must be primary interface of new GamepadEvent("gamepad")] expected: FAIL From 6521c3c580233ae9634cd9c9735cdc9cd024f167 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Wed, 10 Apr 2024 10:51:07 -1000 Subject: [PATCH 09/15] Handle stopping haptic effects from reset() --- components/constellation/tracing.rs | 3 +- .../script/dom/gamepadhapticactuator.rs | 56 ++++++++++++++++--- components/script/dom/globalscope.rs | 22 ++++++++ components/shared/embedder/lib.rs | 7 ++- components/shared/script/lib.rs | 2 + ports/servoshell/tracing.rs | 3 +- ports/servoshell/webview.rs | 30 ++++++++-- 7 files changed, 106 insertions(+), 17 deletions(-) diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs index fe799a47ec97..f1528cd7a1b4 100644 --- a/components/constellation/tracing.rs +++ b/components/constellation/tracing.rs @@ -240,7 +240,8 @@ mod from_script { Self::OnDevtoolsStarted(..) => target_variant!("OnDevtoolsStarted"), Self::ReadyToPresent(..) => target_variant!("ReadyToPresent"), Self::EventDelivered(..) => target_variant!("EventDelivered"), - Self::GamepadHapticEffect(..) => target_variant!("GamepadHapticEffect"), + Self::PlayGamepadHapticEffect(..) => target_variant!("PlayGamepadHapticEffect"), + Self::StopGamepadHapticEffect(..) => target_variant!("StopGamepadHapticEffect"), } } } diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index 0339590b44bb..63874f230f3a 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -32,6 +32,8 @@ pub struct GamepadHapticActuator { effects: Vec, #[ignore_malloc_size_of = "promises are hard"] playing_effect_promise: DomRefCell>>, + #[ignore_malloc_size_of = "promises are hard"] + reset_result_promise: DomRefCell>>, } impl GamepadHapticActuator { @@ -42,6 +44,7 @@ impl GamepadHapticActuator { // TODO: Determine support from gilrs instead of assuming effects: vec![GamepadHapticEffectType::Dual_rumble], playing_effect_promise: DomRefCell::new(None), + reset_result_promise: DomRefCell::new(None), } } @@ -136,7 +139,7 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { strong_magnitude: *params.strongMagnitude, weak_magnitude: *params.weakMagnitude, }; - let event = EmbedderMsg::GamepadHapticEffect(self.gamepad_index as usize, embedder_traits::GamepadHapticEffectType::DualRumble(params)); + let event = EmbedderMsg::PlayGamepadHapticEffect(self.gamepad_index as usize, embedder_traits::GamepadHapticEffectType::DualRumble(params)); self.global().as_window().send_to_embedder(event); playing_effect_promise @@ -151,13 +154,15 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { reset_result_promise.reject_error(Error::InvalidState); } + if self.playing_effect_promise.borrow().is_some() { - // TODO: Stop existing haptic effect - let completed_successfully = true; - if completed_successfully { - let message = DOMString::from("completed"); - reset_result_promise.resolve_native(&message); - } + *self.reset_result_promise.borrow_mut() = Some(reset_result_promise.clone()); + + let event = EmbedderMsg::StopGamepadHapticEffect(self.gamepad_index as usize); + self.global().as_window().send_to_embedder(event); + } else { + let message = DOMString::from("complete"); + reset_result_promise.resolve_native(&message); } reset_result_promise @@ -172,7 +177,39 @@ impl GamepadHapticActuator { pub fn resolve_playing_effect_promise(&self) { let playing_effect_promise = self.playing_effect_promise.borrow().clone(); if let Some(promise) = playing_effect_promise { - let message = DOMString::from("completed"); + let message = DOMString::from("complete"); + promise.resolve_native(&message); + } + } + + pub fn has_reset_result_promise(&self) -> bool { + self.reset_result_promise.borrow().is_some() + } + + pub fn resolve_reset_result_promise(&self) { + let playing_effect_promise = self.playing_effect_promise.borrow().clone(); + let reset_result_promise = self.reset_result_promise.borrow().clone(); + + if playing_effect_promise.is_some() { + let trusted_promise = TrustedPromise::new( + self.playing_effect_promise + .borrow() + .clone() + .expect("Promise is null!"), + ); + let _ = self.global().gamepad_task_source().queue( + task!(preempt_promise: move || { + let promise = trusted_promise.root(); + let message = DOMString::from("preempted"); + promise.resolve_native(&message); + }), + &self.global(), + ); + *self.playing_effect_promise.borrow_mut() = None; + } + + if let Some(promise) = reset_result_promise { + let message = DOMString::from("complete"); promise.resolve_native(&message); } } @@ -204,6 +241,7 @@ impl GamepadHapticActuator { &self.global(), ); - // TODO: Stop haptic effect + let event = EmbedderMsg::StopGamepadHapticEffect(self.gamepad_index as usize); + self.global().as_window().send_to_embedder(event); } } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index d8cc1f821110..fea4666f1816 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -3130,6 +3130,9 @@ impl GlobalScope { GamepadEvent::HapticEffectCompleted(index) => { self.handle_gamepad_haptic_effect_completed(index.0); }, + GamepadEvent::HapticEffectStopped(index) => { + self.handle_gamepad_haptic_effect_stopped(index.0); + } }; } @@ -3256,6 +3259,25 @@ impl GlobalScope { .expect("Failed to queue gamepad haptic effect completed task."); } + pub fn handle_gamepad_haptic_effect_stopped(&self, index: usize) { + let this = Trusted::new(&*self); + self.gamepad_task_source() + .queue_with_canceller( + task!(gamepad_haptic_effect_stopped: move || { + let global = this.root(); + if let Some(window) = global.downcast::() { + if let Some(gamepad) = window.Navigator().get_gamepad(index) { + if gamepad.vibration_actuator().has_reset_result_promise() { + gamepad.vibration_actuator().resolve_reset_result_promise(); + } + } + } + }), + &self.task_canceller(TaskSourceName::Gamepad) + ) + .expect("Failed to queue gamepad haptic effect stopped task."); + } + pub(crate) fn current_group_label(&self) -> Option { self.console_group_stack .borrow() diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs index 44973ff3f50a..db3740c9ceb9 100644 --- a/components/shared/embedder/lib.rs +++ b/components/shared/embedder/lib.rs @@ -215,7 +215,9 @@ pub enum EmbedderMsg { /// The given event was delivered to a pipeline in the given browser. EventDelivered(CompositorEventVariant), /// Request to play a haptic effect on a connected gamepad. - GamepadHapticEffect(usize, GamepadHapticEffectType), + PlayGamepadHapticEffect(usize, GamepadHapticEffectType), + /// Request to stop a haptic effect on a connected gamepad. + StopGamepadHapticEffect(usize), } /// The variant of CompositorEvent that was delivered to a pipeline. @@ -270,7 +272,8 @@ impl Debug for EmbedderMsg { EmbedderMsg::ShowContextMenu(..) => write!(f, "ShowContextMenu"), EmbedderMsg::ReadyToPresent(..) => write!(f, "ReadyToPresent"), EmbedderMsg::EventDelivered(..) => write!(f, "HitTestedEvent"), - EmbedderMsg::GamepadHapticEffect(..) => write!(f, "GamepadHapticEffect"), + EmbedderMsg::PlayGamepadHapticEffect(..) => write!(f, "PlayGamepadHapticEffect"), + EmbedderMsg::StopGamepadHapticEffect(..) => write!(f, "StopGamepadHapticEffect"), } } } diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 332c267ae074..5399b94184b8 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -1354,6 +1354,8 @@ pub enum GamepadEvent { Updated(GamepadIndex, GamepadUpdateType), /// A gamepad haptic effect has completed HapticEffectCompleted(GamepadIndex), + /// A gamepad haptic effect has been stopped + HapticEffectStopped(GamepadIndex), } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/ports/servoshell/tracing.rs b/ports/servoshell/tracing.rs index f0a026143a4b..83bdcb2ce6d9 100644 --- a/ports/servoshell/tracing.rs +++ b/ports/servoshell/tracing.rs @@ -174,7 +174,8 @@ mod from_servo { Self::OnDevtoolsStarted(..) => target!("OnDevtoolsStarted"), Self::ReadyToPresent(..) => target!("ReadyToPresent"), Self::EventDelivered(..) => target!("EventDelivered"), - Self::GamepadHapticEffect(..) => target!("GamepadHapticEffect"), + Self::PlayGamepadHapticEffect(..) => target!("PlayGamepadHapticEffect"), + Self::StopGamepadHapticEffect(..) => target!("StopGamepadHapticEffect"), } } } diff --git a/ports/servoshell/webview.rs b/ports/servoshell/webview.rs index fcd28e2fd330..19f6998f2e46 100644 --- a/ports/servoshell/webview.rs +++ b/ports/servoshell/webview.rs @@ -58,7 +58,7 @@ pub struct WebViewManager { event_queue: Vec, clipboard: Option, gamepad: Option, - haptic_effects: Vec, + haptic_effects: Vec>, shutdown_requested: bool, load_status: LoadStatus, } @@ -209,6 +209,9 @@ where } }, EventType::Connected => { + let len = self.haptic_effects.len(); + self.haptic_effects.resize_with(len + 1, Default::default); + let name = String::from(name); let bounds = GamepadInputBounds { axis_bounds: (-1.0, 1.0), @@ -288,14 +291,30 @@ where .add_gamepad(&connected_gamepad.1) .finish(gilrs) .expect("Failed to create haptic effect, gamepad is either disconnected or doesn't support force feedback."); - self.haptic_effects.push(effect); - self.haptic_effects[0].play().expect("Failed to play haptic effect."); + self.haptic_effects.as_mut_slice()[index] = Some(effect); + self.haptic_effects[index].as_ref().expect("No haptic effect found").play().expect("Failed to play haptic effect."); } else { println!("Couldn't find connected gamepad to play haptic effect on"); } } } + fn stop_haptic_effect(&mut self, index: usize) { + let Some(ref effect) = self.haptic_effects[index] else { + return; + }; + + effect.stop().expect("Failed to stop haptic effect."); + self.haptic_effects.as_mut_slice()[index] = None; + + if self.haptic_effects.iter().all(Option::is_none) { + self.haptic_effects.clear(); + } + + let event = GamepadEvent::HapticEffectStopped(GamepadIndex(index)); + self.event_queue.push(EmbedderEvent::Gamepad(event)); + } + pub fn shutdown_requested(&self) -> bool { self.shutdown_requested } @@ -781,11 +800,14 @@ where .push(EmbedderEvent::FocusWebView(webview_id)); } }, - EmbedderMsg::GamepadHapticEffect(index, effect) => match effect { + EmbedderMsg::PlayGamepadHapticEffect(index, effect) => match effect { GamepadHapticEffectType::DualRumble(params) => { self.play_haptic_effect(index, params) }, }, + EmbedderMsg::StopGamepadHapticEffect(index) => { + self.stop_haptic_effect(index) + } } } From 54be0dbc888e7de517d75db959db7f6ab759ee70 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Wed, 10 Apr 2024 11:22:37 -1000 Subject: [PATCH 10/15] ./mach fmt, fix test-tidy issues --- components/script/dom/gamepadhapticactuator.rs | 10 ++++++---- components/script/dom/globalscope.rs | 4 ++-- .../dom/webidls/GamepadHapticActuator.webidl | 8 ++++---- ports/servoshell/webview.rs | 14 ++++++++------ 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index 63874f230f3a..ecea5d60948d 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -100,7 +100,7 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { "Weak magnitude value is not within range of 0.0 to 1.0.".to_string(), )); } - } + }, } let document = self.global().as_window().Document(); @@ -139,7 +139,10 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { strong_magnitude: *params.strongMagnitude, weak_magnitude: *params.weakMagnitude, }; - let event = EmbedderMsg::PlayGamepadHapticEffect(self.gamepad_index as usize, embedder_traits::GamepadHapticEffectType::DualRumble(params)); + let event = EmbedderMsg::PlayGamepadHapticEffect( + self.gamepad_index as usize, + embedder_traits::GamepadHapticEffectType::DualRumble(params), + ); self.global().as_window().send_to_embedder(event); playing_effect_promise @@ -154,7 +157,6 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { reset_result_promise.reject_error(Error::InvalidState); } - if self.playing_effect_promise.borrow().is_some() { *self.reset_result_promise.borrow_mut() = Some(reset_result_promise.clone()); @@ -193,7 +195,7 @@ impl GamepadHapticActuator { if playing_effect_promise.is_some() { let trusted_promise = TrustedPromise::new( self.playing_effect_promise - .borrow() + .borrow() .clone() .expect("Promise is null!"), ); diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index fea4666f1816..a83926960cf8 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -3132,7 +3132,7 @@ impl GlobalScope { }, GamepadEvent::HapticEffectStopped(index) => { self.handle_gamepad_haptic_effect_stopped(index.0); - } + }, }; } @@ -3273,7 +3273,7 @@ impl GlobalScope { } } }), - &self.task_canceller(TaskSourceName::Gamepad) + &self.task_canceller(TaskSourceName::Gamepad), ) .expect("Failed to queue gamepad haptic effect stopped task."); } diff --git a/components/script/dom/webidls/GamepadHapticActuator.webidl b/components/script/dom/webidls/GamepadHapticActuator.webidl index 8eb7fe1d2fb1..a1277e16fecb 100644 --- a/components/script/dom/webidls/GamepadHapticActuator.webidl +++ b/components/script/dom/webidls/GamepadHapticActuator.webidl @@ -2,7 +2,7 @@ * 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/. */ -// https://www.w3.org/TR/gamepad/#gamepadhapticactuator-interface +// https://w3c.github.io/gamepad/#gamepadhapticactuator-interface [Exposed=Window, Pref="dom.gamepad.enabled"] interface GamepadHapticActuator { /* [SameObject] */ readonly attribute /* FrozenArray */ any effects; @@ -13,19 +13,19 @@ interface GamepadHapticActuator { Promise reset(); }; -// https://www.w3.org/TR/gamepad/#gamepadhapticsresult-enum +// https://w3c.github.io/gamepad/#gamepadhapticsresult-enum enum GamepadHapticsResult { "complete", "preempted" }; -// https://www.w3.org/TR/gamepad/#dom-gamepadhapticeffecttype +// https://w3c.github.io/gamepad/#dom-gamepadhapticeffecttype enum GamepadHapticEffectType { "dual-rumble", "trigger-rumble" }; -// https://www.w3.org/TR/gamepad/#dom-gamepadeffectparameters +// https://w3c.github.io/gamepad/#dom-gamepadeffectparameters dictionary GamepadEffectParameters { unsigned long long duration = 0; unsigned long long startDelay = 0; diff --git a/ports/servoshell/webview.rs b/ports/servoshell/webview.rs index 19f6998f2e46..5177f734b214 100644 --- a/ports/servoshell/webview.rs +++ b/ports/servoshell/webview.rs @@ -224,7 +224,7 @@ where }, EventType::ForceFeedbackEffectCompleted => { gamepad_event = Some(GamepadEvent::HapticEffectCompleted(index)); - } + }, _ => {}, } @@ -290,9 +290,13 @@ where .repeat(Repeat::For(start_delay + duration)) .add_gamepad(&connected_gamepad.1) .finish(gilrs) - .expect("Failed to create haptic effect, gamepad is either disconnected or doesn't support force feedback."); + .expect("Failed to create haptic effect, ensure connected gamepad supports force feedback."); self.haptic_effects.as_mut_slice()[index] = Some(effect); - self.haptic_effects[index].as_ref().expect("No haptic effect found").play().expect("Failed to play haptic effect."); + self.haptic_effects[index] + .as_ref() + .expect("No haptic effect found") + .play() + .expect("Failed to play haptic effect."); } else { println!("Couldn't find connected gamepad to play haptic effect on"); } @@ -805,9 +809,7 @@ where self.play_haptic_effect(index, params) }, }, - EmbedderMsg::StopGamepadHapticEffect(index) => { - self.stop_haptic_effect(index) - } + EmbedderMsg::StopGamepadHapticEffect(index) => self.stop_haptic_effect(index), } } From efcebc6f449dc8cd1a0a1628f3f7b0d1edbaae57 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Wed, 10 Apr 2024 12:53:44 -1000 Subject: [PATCH 11/15] Add extra validity checks for trigger rumble --- components/script/dom/gamepadhapticactuator.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index ecea5d60948d..45cd04270a12 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -99,6 +99,14 @@ impl GamepadHapticActuatorMethods for GamepadHapticActuator { playing_effect_promise.reject_error(Error::Type( "Weak magnitude value is not within range of 0.0 to 1.0.".to_string(), )); + } 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(), + )); + } 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(), + )); } }, } From 7184ff0a38f8b97b8367a31d1db50520ca8da0b6 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Wed, 10 Apr 2024 13:49:06 -1000 Subject: [PATCH 12/15] Retrieve supported haptic effects from embedder --- components/script/dom/gamepad.rs | 16 +++++++-- .../script/dom/gamepadhapticactuator.rs | 35 +++++++++++++++---- components/script/dom/globalscope.rs | 13 ++++--- components/shared/script/lib.rs | 16 ++++++++- ports/servoshell/webview.rs | 15 ++++++-- 5 files changed, 78 insertions(+), 17 deletions(-) diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs index 6521b0204b09..8f0edc4959c3 100644 --- a/components/script/dom/gamepad.rs +++ b/components/script/dom/gamepad.rs @@ -6,7 +6,7 @@ use std::cell::Cell; use dom_struct::dom_struct; use js::typedarray::{Float64, Float64Array}; -use script_traits::GamepadUpdateType; +use script_traits::{GamepadSupportedHapticEffects, GamepadUpdateType}; use super::bindings::buffer_source::HeapBufferSource; use crate::dom::bindings::codegen::Bindings::GamepadBinding::{GamepadHand, GamepadMethods}; @@ -94,8 +94,16 @@ impl Gamepad { id: String, axis_bounds: (f64, f64), button_bounds: (f64, f64), + supported_haptic_effects: GamepadSupportedHapticEffects, ) -> DomRoot { - Self::new_with_proto(global, gamepad_id, id, axis_bounds, button_bounds) + Self::new_with_proto( + global, + gamepad_id, + id, + axis_bounds, + button_bounds, + supported_haptic_effects, + ) } /// When we construct a new gamepad, we initialize the number of buttons and @@ -109,9 +117,11 @@ impl Gamepad { id: String, axis_bounds: (f64, f64), button_bounds: (f64, f64), + supported_haptic_effects: GamepadSupportedHapticEffects, ) -> DomRoot { let button_list = GamepadButtonList::init_buttons(global); - let vibration_actuator = GamepadHapticActuator::new(global, gamepad_id); + let vibration_actuator = + GamepadHapticActuator::new(global, gamepad_id, supported_haptic_effects); let gamepad = reflect_dom_object_with_proto( Box::new(Gamepad::new_inherited( gamepad_id, diff --git a/components/script/dom/gamepadhapticactuator.rs b/components/script/dom/gamepadhapticactuator.rs index 45cd04270a12..08ccb156bf57 100644 --- a/components/script/dom/gamepadhapticactuator.rs +++ b/components/script/dom/gamepadhapticactuator.rs @@ -7,6 +7,7 @@ use std::rc::Rc; use dom_struct::dom_struct; use embedder_traits::{DualRumbleEffectParams, EmbedderMsg}; use js::jsval::JSVal; +use script_traits::GamepadSupportedHapticEffects; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::GamepadHapticActuatorBinding::{ @@ -37,24 +38,44 @@ pub struct GamepadHapticActuator { } impl GamepadHapticActuator { - fn new_inherited(gamepad_index: u32) -> 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: gamepad_index.into(), - // TODO: Determine support from gilrs instead of assuming - effects: vec![GamepadHapticEffectType::Dual_rumble], + effects, playing_effect_promise: DomRefCell::new(None), reset_result_promise: DomRefCell::new(None), } } - pub fn new(global: &GlobalScope, gamepad_index: u32) -> DomRoot { - Self::new_with_proto(global, gamepad_index) + pub fn new( + global: &GlobalScope, + gamepad_index: u32, + supported_haptic_effects: GamepadSupportedHapticEffects, + ) -> DomRoot { + Self::new_with_proto(global, gamepad_index, supported_haptic_effects) } - fn new_with_proto(global: &GlobalScope, gamepad_index: u32) -> DomRoot { + fn new_with_proto( + global: &GlobalScope, + gamepad_index: u32, + supported_haptic_effects: GamepadSupportedHapticEffects, + ) -> DomRoot { let haptic_actuator = reflect_dom_object_with_proto( - Box::new(GamepadHapticActuator::new_inherited(gamepad_index)), + Box::new(GamepadHapticActuator::new_inherited( + gamepad_index, + supported_haptic_effects, + )), global, None, ); diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index a83926960cf8..8c050965931c 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -51,8 +51,9 @@ use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_tim use script_traits::serializable::{BlobData, BlobImpl, FileBlob}; use script_traits::transferable::MessagePortImpl; use script_traits::{ - BroadcastMsg, GamepadEvent, GamepadUpdateType, MessagePortMsg, MsDuration, PortMessageTask, - ScriptMsg, ScriptToConstellationChan, TimerEvent, TimerEventId, TimerSchedulerMsg, TimerSource, + BroadcastMsg, GamepadEvent, GamepadSupportedHapticEffects, GamepadUpdateType, MessagePortMsg, + MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent, TimerEventId, + TimerSchedulerMsg, TimerSource, }; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use uuid::Uuid; @@ -3113,12 +3114,13 @@ impl GlobalScope { pub fn handle_gamepad_event(&self, gamepad_event: GamepadEvent) { match gamepad_event { - GamepadEvent::Connected(index, name, bounds) => { + GamepadEvent::Connected(index, name, bounds, supported_haptic_effects) => { self.handle_gamepad_connect( index.0, name, bounds.axis_bounds, bounds.button_bounds, + supported_haptic_effects, ); }, GamepadEvent::Disconnected(index) => { @@ -3146,6 +3148,7 @@ impl GlobalScope { name: String, axis_bounds: (f64, f64), button_bounds: (f64, f64), + supported_haptic_effects: GamepadSupportedHapticEffects, ) { // TODO: 2. If document is not null and is not allowed to use the "gamepad" permission, // then abort these steps. @@ -3157,7 +3160,9 @@ impl GlobalScope { if let Some(window) = global.downcast::() { let navigator = window.Navigator(); let selected_index = navigator.select_gamepad_index(); - let gamepad = Gamepad::new(&global, selected_index, name, axis_bounds, button_bounds); + let gamepad = Gamepad::new( + &global, selected_index, name, axis_bounds, button_bounds, supported_haptic_effects + ); navigator.set_gamepad(selected_index as usize, &*gamepad); } }), diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 5399b94184b8..ebcd8736dabe 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -1340,12 +1340,26 @@ pub struct GamepadInputBounds { pub button_bounds: (f64, f64), } +#[derive(Clone, Debug, Deserialize, Serialize)] +/// The haptic effects supported by this gamepad +pub struct GamepadSupportedHapticEffects { + /// Gamepad support for dual rumble effects + pub supports_dual_rumble: bool, + /// Gamepad support for trigger rumble effects + pub supports_trigger_rumble: bool, +} + #[derive(Clone, Debug, Deserialize, Serialize)] /// The type of Gamepad event pub enum GamepadEvent { /// A new gamepad has been connected /// - Connected(GamepadIndex, String, GamepadInputBounds), + Connected( + GamepadIndex, + String, + GamepadInputBounds, + GamepadSupportedHapticEffects, + ), /// An existing gamepad has been disconnected /// Disconnected(GamepadIndex), diff --git a/ports/servoshell/webview.rs b/ports/servoshell/webview.rs index 5177f734b214..d9bd2afacada 100644 --- a/ports/servoshell/webview.rs +++ b/ports/servoshell/webview.rs @@ -24,7 +24,8 @@ use servo::embedder_traits::{ }; use servo::msg::constellation_msg::{TopLevelBrowsingContextId as WebViewId, TraversalDirection}; use servo::script_traits::{ - GamepadEvent, GamepadIndex, GamepadInputBounds, GamepadUpdateType, TouchEventType, + GamepadEvent, GamepadIndex, GamepadInputBounds, GamepadSupportedHapticEffects, + GamepadUpdateType, TouchEventType, }; use servo::servo_config::opts; use servo::servo_url::ServoUrl; @@ -217,7 +218,17 @@ where axis_bounds: (-1.0, 1.0), button_bounds: (0.0, 1.0), }; - gamepad_event = Some(GamepadEvent::Connected(index, name, bounds)); + // GilRs does not yet support trigger rumble + let supported_haptic_effects = GamepadSupportedHapticEffects { + supports_dual_rumble: true, + supports_trigger_rumble: false, + }; + gamepad_event = Some(GamepadEvent::Connected( + index, + name, + bounds, + supported_haptic_effects, + )); }, EventType::Disconnected => { gamepad_event = Some(GamepadEvent::Disconnected(index)); From 64fe0f39b13c1d52eefee933d32b94e4cbcd96c1 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Wed, 10 Apr 2024 15:17:45 -1000 Subject: [PATCH 13/15] Fix test expectations --- .../wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini | 6 ++++++ tests/wpt/meta/gamepad/idlharness.window.js.ini | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/tests/wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini b/tests/wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini index 885d39022dbf..577d82d9237b 100644 --- a/tests/wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini +++ b/tests/wpt/meta-legacy-layout/gamepad/idlharness.window.js.ini @@ -1,4 +1,10 @@ [idlharness.window.html] + [GamepadHapticActuator interface: operation playEffect(GamepadHapticEffectType, optional GamepadEffectParameters)] + expected: FAIL + + [GamepadHapticActuator interface: operation reset()] + expected: FAIL + [GamepadEvent must be primary interface of new GamepadEvent("gamepad")] expected: FAIL diff --git a/tests/wpt/meta/gamepad/idlharness.window.js.ini b/tests/wpt/meta/gamepad/idlharness.window.js.ini index 885d39022dbf..577d82d9237b 100644 --- a/tests/wpt/meta/gamepad/idlharness.window.js.ini +++ b/tests/wpt/meta/gamepad/idlharness.window.js.ini @@ -1,4 +1,10 @@ [idlharness.window.html] + [GamepadHapticActuator interface: operation playEffect(GamepadHapticEffectType, optional GamepadEffectParameters)] + expected: FAIL + + [GamepadHapticActuator interface: operation reset()] + expected: FAIL + [GamepadEvent must be primary interface of new GamepadEvent("gamepad")] expected: FAIL From f2331e5afe4f025d9cc0f9d5c5b4e049527116f6 Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Wed, 10 Apr 2024 15:39:59 -1000 Subject: [PATCH 14/15] Add missing spec link, pin gilrs commit --- Cargo.lock | 4 ++-- components/script/dom/gamepad.rs | 1 + ports/servoshell/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8b2fe175892..0ecc4142d605 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1994,7 +1994,7 @@ dependencies = [ [[package]] name = "gilrs" version = "0.10.6" -source = "git+https://gitlab.com/gilrs-project/gilrs#eba723a27a324a631cb65c62c9410a4f928d4da2" +source = "git+https://gitlab.com/gilrs-project/gilrs?rev=09576ad0fb29db4a9238d34b86e7a5eaf628ee95#09576ad0fb29db4a9238d34b86e7a5eaf628ee95" dependencies = [ "fnv", "gilrs-core", @@ -2006,7 +2006,7 @@ dependencies = [ [[package]] name = "gilrs-core" version = "0.5.11" -source = "git+https://gitlab.com/gilrs-project/gilrs#eba723a27a324a631cb65c62c9410a4f928d4da2" +source = "git+https://gitlab.com/gilrs-project/gilrs?rev=09576ad0fb29db4a9238d34b86e7a5eaf628ee95#09576ad0fb29db4a9238d34b86e7a5eaf628ee95" dependencies = [ "core-foundation", "inotify", diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs index 8f0edc4959c3..951be3d05d53 100644 --- a/components/script/dom/gamepad.rs +++ b/components/script/dom/gamepad.rs @@ -181,6 +181,7 @@ impl GamepadMethods for Gamepad { DomRoot::from_ref(&*self.buttons) } + // https://w3c.github.io/gamepad/#dom-gamepad-vibrationactuator fn VibrationActuator(&self) -> DomRoot { DomRoot::from_ref(&*self.vibration_actuator) } diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index 2b5759a49d87..1aa3246fa5d9 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -62,7 +62,7 @@ egui_glow = { version = "0.22.0", features = ["winit"] } egui-winit = { version = "0.22.0", default-features = false, features = ["clipboard", "wayland"] } euclid = { workspace = true } getopts = { workspace = true } -gilrs = { git = "https://gitlab.com/gilrs-project/gilrs" } +gilrs = { git = "https://gitlab.com/gilrs-project/gilrs", rev="09576ad0fb29db4a9238d34b86e7a5eaf628ee95"} gleam = { workspace = true } glow = "0.12.2" keyboard-types = { workspace = true } From d994955115c97b629930357ef16f436fe4c352ad Mon Sep 17 00:00:00 2001 From: Daniel Adams Date: Wed, 10 Apr 2024 15:47:45 -1000 Subject: [PATCH 15/15] servoshell cargo formatting --- ports/servoshell/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index 1aa3246fa5d9..9309444c951b 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -62,7 +62,7 @@ egui_glow = { version = "0.22.0", features = ["winit"] } egui-winit = { version = "0.22.0", default-features = false, features = ["clipboard", "wayland"] } euclid = { workspace = true } getopts = { workspace = true } -gilrs = { git = "https://gitlab.com/gilrs-project/gilrs", rev="09576ad0fb29db4a9238d34b86e7a5eaf628ee95"} +gilrs = { git = "https://gitlab.com/gilrs-project/gilrs", rev = "09576ad0fb29db4a9238d34b86e7a5eaf628ee95" } gleam = { workspace = true } glow = "0.12.2" keyboard-types = { workspace = true }