diff options
author | Daniel Adams <70986246+msub2@users.noreply.github.com> | 2024-07-19 20:29:27 -1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-20 06:29:27 +0000 |
commit | 2c17de7fa72ca6f96f4e37faf90ac9786d0b53a0 (patch) | |
tree | 3f4b1d4691e1170c3d7106ade77d10fa486c634f /ports/servoshell | |
parent | 9212ed203a7dcec88008fca47bce0bff3fe2649b (diff) | |
download | servo-2c17de7fa72ca6f96f4e37faf90ac9786d0b53a0.tar.gz servo-2c17de7fa72ca6f96f4e37faf90ac9786d0b53a0.zip |
Gamepad: Implement GamepadHapticActuator (#32046)
* Implement Servo side of GamepadHapticActuator
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Get build working
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Create effect handling on embedder side
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update tracing for GamepadHapticEffect
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update gilrs to point to commit with effect complete event
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Implement playing and preempting haptic effects
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update IDL to add trigger rumble
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update WPT expectations
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Handle stopping haptic effects from reset()
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* ./mach fmt, fix test-tidy issues
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Add extra validity checks for trigger rumble
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Retrieve supported haptic effects from embedder
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Fix test expectations
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Add missing spec link, pin gilrs commit
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* servoshell cargo formatting
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Fix Cargo.toml
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Additional comments, realm proof, naming
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* ./mach fmt
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Update gilrs rev to gilrs-core 0.5.12 release
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Implement sequence ids for gamepad haptic promises
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Take playing effect promise instead of cloning
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Implement listener for reset function
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Fix Cargo.lock
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Restructure IPC listeners, add comments, handle visibility change
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Check that haptic effect still exists before handling ff completion event
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Visibility steps, add InRealm bindings for promises
Signed-off-by: Daniel Adams <msub2official@gmail.com>
* Add Gamepad EmbedderMsg arms to egl servo_glue
Signed-off-by: Daniel Adams <msub2official@gmail.com>
---------
Signed-off-by: Daniel Adams <msub2official@gmail.com>
Diffstat (limited to 'ports/servoshell')
-rw-r--r-- | ports/servoshell/Cargo.toml | 2 | ||||
-rw-r--r-- | ports/servoshell/desktop/tracing.rs | 2 | ||||
-rw-r--r-- | ports/servoshell/desktop/webview.rs | 128 | ||||
-rw-r--r-- | ports/servoshell/egl/servo_glue.rs | 4 |
4 files changed, 129 insertions, 7 deletions
diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index be995af59a1..590b6f07f99 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -98,7 +98,7 @@ egui = { version = "0.28.1" } egui_glow = { version = "0.28.1", features = ["winit"] } egui-winit = { version = "0.28.1", default-features = false, features = ["clipboard", "wayland"] } euclid = { workspace = true } -gilrs = "0.10.8" +gilrs = { git = "https://gitlab.com/gilrs-project/gilrs", rev = "eafb7f2ef488874188c5d75adce9aef486be9d4e" } gleam = { workspace = true } glow = "0.13.1" keyboard-types = { workspace = true } diff --git a/ports/servoshell/desktop/tracing.rs b/ports/servoshell/desktop/tracing.rs index 59c07105c94..2edddf645cc 100644 --- a/ports/servoshell/desktop/tracing.rs +++ b/ports/servoshell/desktop/tracing.rs @@ -176,6 +176,8 @@ mod from_servo { Self::OnDevtoolsStarted(..) => target!("OnDevtoolsStarted"), Self::ReadyToPresent(..) => target!("ReadyToPresent"), Self::EventDelivered(..) => target!("EventDelivered"), + Self::PlayGamepadHapticEffect(..) => target!("PlayGamepadHapticEffect"), + Self::StopGamepadHapticEffect(..) => target!("StopGamepadHapticEffect"), } } } diff --git a/ports/servoshell/desktop/webview.rs b/ports/servoshell/desktop/webview.rs index 920a6a61477..558cf52e95e 100644 --- a/ports/servoshell/desktop/webview.rs +++ b/ports/servoshell/desktop/webview.rs @@ -12,18 +12,21 @@ use std::{env, thread}; use arboard::Clipboard; use euclid::{Point2D, Vector2D}; +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}; use servo::base::id::TopLevelBrowsingContextId as WebViewId; 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::ipc_channel::ipc::IpcSender; use servo::script_traits::{ - GamepadEvent, GamepadIndex, GamepadInputBounds, GamepadUpdateType, TouchEventType, - TraversalDirection, + GamepadEvent, GamepadIndex, GamepadInputBounds, GamepadSupportedHapticEffects, + GamepadUpdateType, TouchEventType, TraversalDirection, }; use servo::servo_config::opts; use servo::servo_url::ServoUrl; @@ -59,6 +62,7 @@ pub struct WebViewManager<Window: WindowPortsMethods + ?Sized> { event_queue: Vec<EmbedderEvent>, clipboard: Option<Clipboard>, gamepad: Option<Gilrs>, + haptic_effects: HashMap<usize, HapticEffect>, shutdown_requested: bool, load_status: LoadStatus, } @@ -80,6 +84,11 @@ pub enum LoadStatus { LoadComplete, } +pub struct HapticEffect { + pub effect: Effect, + pub sender: IpcSender<bool>, +} + impl<Window> WebViewManager<Window> where Window: WindowPortsMethods + ?Sized, @@ -108,6 +117,8 @@ where None }, }, + haptic_effects: HashMap::default(), + event_queue: Vec::new(), shutdown_requested: false, load_status: LoadStatus::LoadComplete, @@ -218,11 +229,32 @@ 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)); }, + EventType::ForceFeedbackEffectCompleted => { + let Some(effect) = self.haptic_effects.get(&event.id.into()) else { + warn!("Failed to find haptic effect for id {}", event.id); + return; + }; + effect + .sender + .send(true) + .expect("Failed to send haptic effect completion."); + self.haptic_effects.remove(&event.id.into()); + }, _ => {}, } @@ -258,6 +290,79 @@ where } } + fn play_haptic_effect( + &mut self, + index: usize, + params: DualRumbleEffectParams, + effect_complete_sender: IpcSender<bool>, + ) { + let Some(ref mut gilrs) = self.gamepad else { + debug!("Unable to get gilrs instance!"); + return; + }; + + if let Some(connected_gamepad) = gilrs + .gamepads() + .find(|gamepad| usize::from(gamepad.0) == index) + { + 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: start_delay, + play_for: duration, + with_delay: Ticks::from_ms(0), + }; + let effect = EffectBuilder::new() + .add_effect(BaseEffect { + kind: BaseEffectType::Strong { magnitude: strong_magnitude }, + scheduling, + envelope: Default::default(), + }) + .add_effect(BaseEffect { + kind: BaseEffectType::Weak { magnitude: weak_magnitude }, + scheduling, + envelope: Default::default(), + }) + .repeat(Repeat::For(start_delay + duration)) + .add_gamepad(&connected_gamepad.1) + .finish(gilrs) + .expect("Failed to create haptic effect, ensure connected gamepad supports force feedback."); + self.haptic_effects.insert( + index, + HapticEffect { + effect, + sender: effect_complete_sender, + }, + ); + self.haptic_effects[&index] + .effect + .play() + .expect("Failed to play haptic effect."); + } else { + debug!("Couldn't find connected gamepad to play haptic effect on"); + } + } + + fn stop_haptic_effect(&mut self, index: usize) -> bool { + let Some(haptic_effect) = self.haptic_effects.get(&index) else { + return false; + }; + + let stopped_successfully = match haptic_effect.effect.stop() { + Ok(()) => true, + Err(e) => { + debug!("Failed to stop haptic effect: {:?}", e); + false + }, + }; + self.haptic_effects.remove(&index); + + stopped_successfully + } + pub fn shutdown_requested(&self) -> bool { self.shutdown_requested } @@ -744,6 +849,19 @@ where .push(EmbedderEvent::FocusWebView(webview_id)); } }, + EmbedderMsg::PlayGamepadHapticEffect(index, effect, effect_complete_sender) => { + match effect { + GamepadHapticEffectType::DualRumble(params) => { + self.play_haptic_effect(index, params, effect_complete_sender); + }, + } + }, + EmbedderMsg::StopGamepadHapticEffect(index, haptic_stop_sender) => { + let stopped_successfully = self.stop_haptic_effect(index); + haptic_stop_sender + .send(stopped_successfully) + .expect("Failed to send haptic stop result"); + }, } } diff --git a/ports/servoshell/egl/servo_glue.rs b/ports/servoshell/egl/servo_glue.rs index 882451a4196..13022145fe5 100644 --- a/ports/servoshell/egl/servo_glue.rs +++ b/ports/servoshell/egl/servo_glue.rs @@ -623,7 +623,9 @@ impl ServoGlue { EmbedderMsg::HeadParsed | EmbedderMsg::SetFullscreenState(..) | EmbedderMsg::ReportProfile(..) | - EmbedderMsg::EventDelivered(..) => {}, + EmbedderMsg::EventDelivered(..) | + EmbedderMsg::PlayGamepadHapticEffect(..) | + EmbedderMsg::StopGamepadHapticEffect(..) => {}, } } |