diff --git a/Cargo.lock b/Cargo.lock index 3a2e1b88b9afe..07e06babb7a4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2046,6 +2046,40 @@ dependencies = [ "weezl", ] +[[package]] +name = "gilrs" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b2e57a9cb946b5d04ae8638c5f554abb5a9f82c4c950fd5b1fee6d119592fb" +dependencies = [ + "fnv", + "gilrs-core", + "log", + "uuid", + "vec_map", +] + +[[package]] +name = "gilrs-core" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0af1827b7dd2f36d740ae804c1b3ea0d64c12533fb61ff91883005143a0e8c5a" +dependencies = [ + "core-foundation", + "inotify", + "io-kit-sys", + "js-sys", + "libc", + "libudev-sys", + "log", + "nix 0.27.1", + "uuid", + "vec_map", + "wasm-bindgen", + "web-sys", + "windows", +] + [[package]] name = "gimli" version = "0.28.1" @@ -2848,6 +2882,26 @@ dependencies = [ "serde", ] +[[package]] +name = "inotify" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -2860,6 +2914,16 @@ dependencies = [ "web-sys", ] +[[package]] +name = "io-kit-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4769cb30e5dcf1710fc6730d3e94f78c47723a014a567de385e113c737394640" +dependencies = [ + "core-foundation-sys", + "mach2", +] + [[package]] name = "io-surface" version = "0.15.1" @@ -3368,6 +3432,16 @@ dependencies = [ "webxr-api", ] +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "libz-sys" version = "1.1.15" @@ -3428,6 +3502,15 @@ dependencies = [ "libc", ] +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -3909,6 +3992,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "libc", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -5432,6 +5526,7 @@ dependencies = [ "egui_glow", "euclid", "getopts", + "gilrs", "gleam", "glow", "image", diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml index 56b81a75f2f6c..bbf4f75a12a92 100644 --- a/ports/servoshell/Cargo.toml +++ b/ports/servoshell/Cargo.toml @@ -52,6 +52,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.4" gleam = { workspace = true } glow = "0.12.2" keyboard-types = { workspace = true } diff --git a/ports/servoshell/app.rs b/ports/servoshell/app.rs index 5221c59d63b3f..5ed79d434da73 100644 --- a/ports/servoshell/app.rs +++ b/ports/servoshell/app.rs @@ -437,6 +437,7 @@ impl App { } // Catch some keyboard events, and push the rest onto the WebViewManager event queue. + webviews.handle_gamepad_events(); webviews.handle_window_events(embedder_events); // Take any new embedder messages from Servo itself. diff --git a/ports/servoshell/webview.rs b/ports/servoshell/webview.rs index 4b21a182beba1..4f0127966a2f7 100644 --- a/ports/servoshell/webview.rs +++ b/ports/servoshell/webview.rs @@ -12,6 +12,7 @@ use std::{env, thread}; use arboard::Clipboard; use euclid::{Point2D, Vector2D}; +use gilrs::{EventType, Gilrs}; use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher}; use log::{debug, error, info, trace, warn}; use servo::compositing::windowing::{EmbedderEvent, WebRenderDebugOption}; @@ -20,7 +21,7 @@ use servo::embedder_traits::{ PermissionRequest, PromptDefinition, PromptOrigin, PromptResult, }; use servo::msg::constellation_msg::{TopLevelBrowsingContextId as WebViewId, TraversalDirection}; -use servo::script_traits::TouchEventType; +use servo::script_traits::{GamepadEvent, GamepadIndex, GamepadUpdateType, TouchEventType}; use servo::servo_config::opts; use servo::servo_url::ServoUrl; use servo::webrender_api::ScrollLocation; @@ -51,6 +52,7 @@ pub struct WebViewManager { window: Rc, event_queue: Vec, clipboard: Option, + gamepad: Option, shutdown_requested: bool, } @@ -82,6 +84,13 @@ where None }, }, + gamepad: match Gilrs::new() { + Ok(g) => Some(g), + Err(e) => { + warn!("Error creating gamepad input connection ({})", e); + None + }, + }, event_queue: Vec::new(), shutdown_requested: false, } @@ -113,6 +122,77 @@ where } } + pub fn handle_gamepad_events(&mut self) { + if let Some(ref mut gilrs) = self.gamepad { + while let Some(event) = gilrs.next_event() { + let gamepad = gilrs.gamepad(event.id); + let name = gamepad.name(); + let index = GamepadIndex(event.id.into()); + match event.event { + EventType::ButtonChanged(button, value, _) => { + let mapped_index: usize = match button { + gilrs::Button::South => 0, + gilrs::Button::East => 1, + gilrs::Button::West => 2, + gilrs::Button::North => 3, + gilrs::Button::LeftTrigger => 4, + gilrs::Button::RightTrigger => 5, + gilrs::Button::LeftTrigger2 => 6, + gilrs::Button::RightTrigger2 => 7, + gilrs::Button::Select => 8, + gilrs::Button::Start => 9, + gilrs::Button::LeftThumb => 10, + gilrs::Button::RightThumb => 11, + gilrs::Button::DPadUp => 12, + gilrs::Button::DPadDown => 13, + gilrs::Button::DPadLeft => 14, + gilrs::Button::DPadRight => 15, + gilrs::Button::Mode => 16, + _ => 17 // Other buttons do not map to "standard" gamepad mapping and are ignored + }; + if mapped_index < 17 { + let update_type = GamepadUpdateType::Button(mapped_index, value); + let event = GamepadEvent::Updated(index, update_type); + self.event_queue.push(EmbedderEvent::Gamepad(event)); + } + }, + EventType::AxisChanged(axis, value, _) => { + let mapped_axis: usize = match axis { + gilrs::Axis::LeftStickX => 0, + gilrs::Axis::LeftStickY => 1, + gilrs::Axis::RightStickX => 2, + gilrs::Axis::RightStickY => 3, + _ => 4, // Other axes do not map to "standard" gamepad mapping and are ignored + }; + if mapped_axis < 4 { + // The Gamepad spec designates down as positive and up as negative. + // GilRs does the inverse of this, so correct for it here. + let axis_value = match mapped_axis { + 0 | 2 => value, + 1 | 3 => -value, + _ => 0. // Should not reach here + }; + let update_type = GamepadUpdateType::Axis(mapped_axis, axis_value); + let event = GamepadEvent::Updated(index, update_type); + self.event_queue.push(EmbedderEvent::Gamepad(event)); + } + }, + EventType::Connected => { + println!("Gamepad was connected, firing GamepadEvent::Connected"); + let name = String::from(name); + let event = GamepadEvent::Connected(index, name); + self.event_queue.push(EmbedderEvent::Gamepad(event)); + }, + EventType::Disconnected => { + let event = GamepadEvent::Disconnected(index); + self.event_queue.push(EmbedderEvent::Gamepad(event)); + }, + _ => {}, + } + } + } + } + pub fn shutdown_requested(&self) -> bool { self.shutdown_requested }