mirror of
https://github.com/servo/servo.git
synced 2025-08-09 07:25:35 +01:00
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>
This commit is contained in:
parent
9212ed203a
commit
2c17de7fa7
18 changed files with 652 additions and 68 deletions
|
@ -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 }
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -623,7 +623,9 @@ impl ServoGlue {
|
|||
EmbedderMsg::HeadParsed |
|
||||
EmbedderMsg::SetFullscreenState(..) |
|
||||
EmbedderMsg::ReportProfile(..) |
|
||||
EmbedderMsg::EventDelivered(..) => {},
|
||||
EmbedderMsg::EventDelivered(..) |
|
||||
EmbedderMsg::PlayGamepadHapticEffect(..) |
|
||||
EmbedderMsg::StopGamepadHapticEffect(..) => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue