aboutsummaryrefslogtreecommitdiffstats
path: root/ports/servoshell
diff options
context:
space:
mode:
authorDaniel Adams <70986246+msub2@users.noreply.github.com>2024-07-19 20:29:27 -1000
committerGitHub <noreply@github.com>2024-07-20 06:29:27 +0000
commit2c17de7fa72ca6f96f4e37faf90ac9786d0b53a0 (patch)
tree3f4b1d4691e1170c3d7106ade77d10fa486c634f /ports/servoshell
parent9212ed203a7dcec88008fca47bce0bff3fe2649b (diff)
downloadservo-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.toml2
-rw-r--r--ports/servoshell/desktop/tracing.rs2
-rw-r--r--ports/servoshell/desktop/webview.rs128
-rw-r--r--ports/servoshell/egl/servo_glue.rs4
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(..) => {},
}
}