Skip to content

Commit

Permalink
compiles for WASM
Browse files Browse the repository at this point in the history
  • Loading branch information
Yatekii committed Jan 1, 2025
1 parent 3d2eb50 commit 27efde3
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.wasm32-unknown-unknown]
rustflags = "--cfg=web_sys_unstable_apis"
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rust-analyzer.check.targets": ["wasm32-unknown-unknown"],
"rust-analyzer.cargo.target": "wasm32-unknown-unknown",
"rust-analyzer.showUnlinkedFileNotification": false
}
25 changes: 22 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ authors = [
"Artyom Pavlov <[email protected]>",
"mberndt123",
"niklasad1",
"Stefan Kerkmann"
"Stefan Kerkmann",
]
repository = "https://github.com/ruabmbua/hidapi-rs"
description = "Rust-y wrapper around hidapi"
Expand Down Expand Up @@ -60,20 +60,39 @@ windows-native = [
"windows-sys/Win32_Storage_FileSystem",
"windows-sys/Win32_System_IO",
"windows-sys/Win32_System_Threading",
"windows-sys/Win32_UI_Shell_PropertiesSystem"
"windows-sys/Win32_UI_Shell_PropertiesSystem",
]

[dependencies]
libc = "0.2"
cfg-if = "1"
tracing = "0.1"

[target.'cfg(target_os = "linux")'.dependencies]
libc = "0.2"
udev = { version = "0.8", optional = true }
nix = { version = "0.27", optional = true, features = ["fs", "ioctl", "poll"] }

[target.'cfg(target_os = "macos")'.dependencies]
libc = "0.2"


[target.'cfg(windows)'.dependencies]
libc = "0.2"
windows-sys = { version = "0.48", features = ["Win32_Foundation"] }

[target.'cfg(target_family="wasm")'.dependencies]
web-sys = { version = "*", features = [
"Document",
"Window",
"Navigator",
"Usb",
"UsbDevice",
"UsbDeviceRequestOptions",
"Hid",
"HidDevice",
] }
wasm-bindgen-futures = "*"

[build-dependencies]
cc = "1.0"
pkg-config = "0.3"
Expand Down
9 changes: 9 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ extern crate pkg_config;
use std::env;

fn main() {
println!("cargo::rustc-check-cfg=cfg(hidapi)");
println!("cargo::rustc-check-cfg=cfg(libusb)");

let target = env::var("TARGET").unwrap();

if target.contains("linux") {
Expand All @@ -37,6 +40,8 @@ fn main() {
compile_openbsd();
} else if target.contains("illumos") {
compile_illumos();
} else if target.contains("wasm") {
compile_wasm();
} else {
panic!("Unsupported target os for hidapi-rs");
}
Expand Down Expand Up @@ -215,3 +220,7 @@ fn compile_macos() {
println!("cargo:rustc-link-lib=framework=CoreFoundation");
println!("cargo:rustc-link-lib=framework=AppKit")
}

fn compile_wasm() {
// println!("cargo:rustc-cfg=webhid");
}
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// This file is part of hidapi-rs, based on hidapi-rs by Osspial
// **************************************************************************

#[cfg(not(target_family = "wasm"))]
use libc::wchar_t;
use std::error::Error;
use std::fmt::{Display, Formatter, Result};
Expand All @@ -16,6 +17,7 @@ pub enum HidError {
message: String,
},
HidApiErrorEmpty,
#[cfg(not(target_family = "wasm"))]
FromWideCharError {
wide_char: wchar_t,
},
Expand All @@ -42,6 +44,7 @@ impl Display for HidError {
match self {
HidError::HidApiError { message } => write!(f, "hidapi error: {}", message),
HidError::HidApiErrorEmpty => write!(f, "hidapi error: (could not get error message)"),
#[cfg(not(target_family = "wasm"))]
HidError::FromWideCharError { wide_char } => {
write!(f, "failed converting {:#X} to rust char", wide_char)
}
Expand Down
7 changes: 5 additions & 2 deletions src/hidapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ use std::{
fmt::{self, Debug},
};

#[cfg(not(target_family = "wasm"))]
use libc::{c_int, size_t, wchar_t};

use crate::{ffi, DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString};
#[cfg(not(target_family = "wasm"))]
use crate::ffi;
use crate::{DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString};

#[cfg(target_os = "macos")]
mod macos;
Expand All @@ -19,7 +22,7 @@ const STRING_BUF_LEN: usize = 128;
pub struct HidApiBackend;

impl HidApiBackend {
pub fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec<DeviceInfo>> {
pub async fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec<DeviceInfo>> {
let mut device_vector = Vec::with_capacity(8);

let enumeration = unsafe { ffi::hid_enumerate(vid, pid) };
Expand Down
21 changes: 15 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@
//! an opt-in that can be enabled with the `macos-shared-device` feature flag.
mod error;
#[cfg(not(target_family = "wasm"))]
mod ffi;

use cfg_if::cfg_if;
#[cfg(not(target_family = "wasm"))]
use libc::wchar_t;
use std::ffi::CStr;
use std::ffi::CString;
Expand All @@ -85,6 +87,9 @@ cfg_if! {
} else if #[cfg(hidapi)] {
mod hidapi;
use hidapi::HidApiBackend;
} else if #[cfg(target_family="wasm")] {
mod webusb;
use webusb::HidApiBackend;
} else {
compile_error!("No backend selected");
}
Expand Down Expand Up @@ -191,13 +196,13 @@ impl HidApi {
///
/// Panics if hidapi is already initialized in "without enumerate" mode
/// (i.e. if `new_without_enumerate()` has been called before).
pub fn new() -> HidResult<Self> {
pub async fn new() -> HidResult<Self> {
lazy_init(true)?;

let mut api = HidApi {
device_list: Vec::with_capacity(8),
};
api.add_devices(0, 0)?;
api.add_devices(0, 0).await?;
Ok(api)
}

Expand All @@ -220,9 +225,9 @@ impl HidApi {
/// Refresh devices list and information about them (to access them use
/// `device_list()` method)
/// Identical to `reset_devices()` followed by `add_devices(0, 0)`.
pub fn refresh_devices(&mut self) -> HidResult<()> {
pub async fn refresh_devices(&mut self) -> HidResult<()> {
self.reset_devices()?;
self.add_devices(0, 0)?;
self.add_devices(0, 0).await?;
Ok(())
}

Expand All @@ -234,9 +239,9 @@ impl HidApi {

/// Indexes devices that match the given VID and PID filters.
/// 0 indicates no filter.
pub fn add_devices(&mut self, vid: u16, pid: u16) -> HidResult<()> {
pub async fn add_devices(&mut self, vid: u16, pid: u16) -> HidResult<()> {
self.device_list
.append(&mut HidApiBackend::get_hid_device_info_vector(vid, pid)?);
.append(&mut HidApiBackend::get_hid_device_info_vector(vid, pid).await?);
Ok(())
}

Expand Down Expand Up @@ -307,6 +312,7 @@ impl HidApi {
enum WcharString {
String(String),
#[cfg_attr(all(feature = "linux-native", target_os = "linux"), allow(dead_code))]
#[cfg(not(target_family = "wasm"))]
Raw(Vec<wchar_t>),
None,
}
Expand Down Expand Up @@ -374,6 +380,7 @@ impl DeviceInfo {
}
}

#[cfg(not(target_family = "wasm"))]
pub fn serial_number_raw(&self) -> Option<&[wchar_t]> {
match self.serial_number {
WcharString::Raw(ref s) => Some(s),
Expand All @@ -393,6 +400,7 @@ impl DeviceInfo {
}
}

#[cfg(not(target_family = "wasm"))]
pub fn manufacturer_string_raw(&self) -> Option<&[wchar_t]> {
match self.manufacturer_string {
WcharString::Raw(ref s) => Some(s),
Expand All @@ -408,6 +416,7 @@ impl DeviceInfo {
}
}

#[cfg(not(target_family = "wasm"))]
pub fn product_string_raw(&self) -> Option<&[wchar_t]> {
match self.product_string {
WcharString::Raw(ref s) => Some(s),
Expand Down
123 changes: 123 additions & 0 deletions src/webusb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use core::cell::Ref;
use core::ffi::CStr;
use std::ffi::CString;

use wasm_bindgen_futures::{js_sys::Array, wasm_bindgen::JsCast, JsFuture};

use crate::{DeviceInfo, HidDeviceBackendBase, HidResult};

pub struct HidApiBackend;

impl HidApiBackend {
pub async fn get_hid_device_info_vector(vid: u16, pid: u16) -> HidResult<Vec<DeviceInfo>> {
let window = web_sys::window().unwrap();
let navigator = window.navigator();
let hid = navigator.hid();
let devices = JsFuture::from(hid.get_devices()).await.unwrap();

let devices: Array = JsCast::unchecked_from_js(devices);

let mut result = vec![];
for device in devices {
let device: web_sys::HidDevice = JsCast::unchecked_from_js(device);

// vid = 0 and pid = 0 means no filter
if (device.vendor_id() != vid && vid != 0) || (device.product_id() != pid && pid != 0) {
continue;
}

result.push(DeviceInfo {
path: CString::new("").unwrap(),
vendor_id: device.vendor_id(),
product_id: device.product_id(),
serial_number: crate::WcharString::None,
release_number: 0,
manufacturer_string: crate::WcharString::None,
product_string: crate::WcharString::String(device.product_name()),
usage_page: 0,
usage: 0,
interface_number: 0,
bus_type: crate::BusType::Usb,
});
}

Ok(result)
}

pub fn open(vid: u16, pid: u16) -> HidResult<HidDevice> {
HidDevice::open(vid, pid, None)
}

pub fn open_serial(vid: u16, pid: u16, sn: &str) -> HidResult<HidDevice> {
HidDevice::open(vid, pid, Some(sn))
}

pub fn open_path(device_path: &CStr) -> HidResult<HidDevice> {
HidDevice::open_path(device_path)
}
}

pub struct HidDevice {}

unsafe impl Send for HidDevice {}

// API for the library to call us, or for internal uses
impl HidDevice {
pub(crate) fn open(_vid: u16, _pid: u16, _sn: Option<&str>) -> HidResult<Self> {
todo!()
}

pub(crate) fn open_path(_device_path: &CStr) -> HidResult<HidDevice> {
todo!()
}

fn _info(&self) -> HidResult<Ref<DeviceInfo>> {
todo!()
}
}

impl HidDeviceBackendBase for HidDevice {
fn write(&self, _data: &[u8]) -> HidResult<usize> {
todo!()
}

fn read(&self, _buf: &mut [u8]) -> HidResult<usize> {
todo!()
}

fn read_timeout(&self, _buf: &mut [u8], _timeout: i32) -> HidResult<usize> {
todo!()
}

fn send_feature_report(&self, _data: &[u8]) -> HidResult<()> {
todo!()
}

fn get_feature_report(&self, _buf: &mut [u8]) -> HidResult<usize> {
todo!()
}

fn set_blocking_mode(&self, _blocking: bool) -> HidResult<()> {
todo!()
}

fn get_manufacturer_string(&self) -> HidResult<Option<String>> {
todo!()
}

fn get_product_string(&self) -> HidResult<Option<String>> {
todo!()
}

fn get_serial_number_string(&self) -> HidResult<Option<String>> {
todo!()
}

fn get_device_info(&self) -> HidResult<DeviceInfo> {
todo!()
}

fn get_report_descriptor(&self, _buf: &mut [u8]) -> HidResult<usize> {
todo!()
}
}

0 comments on commit 27efde3

Please sign in to comment.