pub mod bluetooth;
pub mod combined;
#[cfg(target_os = "macos")]
pub mod iokit;
pub mod usb;
use crate::error::Result;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum DeviceIdentifier {
Usb { product_id: u16 },
Bluetooth { device_name: String },
}
impl DeviceIdentifier {
pub fn usb(product_id: u16) -> Self {
DeviceIdentifier::Usb { product_id }
}
pub fn bluetooth(device_name: impl Into<String>) -> Self {
DeviceIdentifier::Bluetooth {
device_name: device_name.into(),
}
}
pub fn display_name(&self) -> String {
match self {
DeviceIdentifier::Usb { product_id } => format!("USB (Product ID: {})", product_id),
DeviceIdentifier::Bluetooth { device_name } => {
format!("Bluetooth (Name: {})", device_name)
}
}
}
pub fn matches(&self, other: &DeviceIdentifier) -> bool {
match (self, other) {
(
DeviceIdentifier::Usb { product_id: id1 },
DeviceIdentifier::Usb { product_id: id2 },
) => id1 == id2,
(
DeviceIdentifier::Bluetooth { device_name: name1 },
DeviceIdentifier::Bluetooth { device_name: name2 },
) => {
let name1_lower = name1.to_ascii_lowercase();
let name2_lower = name2.to_ascii_lowercase();
!name1_lower.is_empty()
&& !name2_lower.is_empty()
&& (name1_lower.contains(&name2_lower) || name2_lower.contains(&name1_lower))
}
_ => false,
}
}
pub fn is_same_device(&self, other: &DeviceIdentifier) -> bool {
match (self, other) {
(
DeviceIdentifier::Usb { product_id: id1 },
DeviceIdentifier::Usb { product_id: id2 },
) => id1 == id2,
(
DeviceIdentifier::Bluetooth { device_name: name1 },
DeviceIdentifier::Bluetooth { device_name: name2 },
) => name1.eq_ignore_ascii_case(name2),
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct KeyboardMapping {
pub name: String,
pub device: DeviceIdentifier,
pub profile: String,
#[serde(default)]
pub priority: i32,
}
impl KeyboardMapping {
pub fn new(
name: impl Into<String>,
device: DeviceIdentifier,
profile: impl Into<String>,
) -> Self {
KeyboardMapping {
name: name.into(),
device,
profile: profile.into(),
priority: 0,
}
}
pub fn with_priority(
name: impl Into<String>,
device: DeviceIdentifier,
profile: impl Into<String>,
priority: i32,
) -> Self {
KeyboardMapping {
name: name.into(),
device,
profile: profile.into(),
priority,
}
}
}
#[derive(Debug, Clone)]
pub enum DeviceEvent {
Connected(DeviceIdentifier),
Disconnected(DeviceIdentifier),
Initial(Vec<DeviceIdentifier>),
}
pub trait DeviceMonitor: Send + Sync {
fn start_monitoring<F>(&self, callback: F) -> Result<()>
where
F: Fn(DeviceEvent) -> Result<()> + Send + Sync + 'static;
}