Skip to content

Commit

Permalink
feat(DBusDevice): add support for analog input translation for dbus t…
Browse files Browse the repository at this point in the history
…arget
  • Loading branch information
ShadowApex committed Feb 25, 2024
1 parent 3b49e8a commit 4e3f66b
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 64 deletions.
201 changes: 143 additions & 58 deletions src/input/event/dbus.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::input::capability::{Capability, Gamepad, GamepadButton};
use crate::input::capability::{Capability, Gamepad, GamepadAxis, GamepadButton};

use super::native::NativeEvent;
use super::native::{InputValue, NativeEvent};

/// Actions represent all possible DBus event actions
#[derive(Debug, Clone)]
Expand All @@ -10,12 +10,20 @@ pub enum Action {
Quick,
Context,
Option,
Select,
Accept,
Back,
ActOn,
Left,
Right,
Up,
Down,
L1,
L2,
L3,
R1,
R2,
R3,
}

impl Action {
Expand All @@ -26,12 +34,20 @@ impl Action {
Action::Quick => "ui_quick",
Action::Context => "ui_context",
Action::Option => "ui_option",
Action::Select => "ui_select",
Action::Accept => "ui_accept",
Action::Back => "ui_back",
Action::ActOn => "ui_action",
Action::Left => "ui_left",
Action::Right => "ui_right",
Action::Up => "ui_up",
Action::Down => "ui_down",
Action::L1 => "ui_l1",
Action::L2 => "ui_l2",
Action::L3 => "ui_l3",
Action::R1 => "ui_r1",
Action::R2 => "ui_r2",
Action::R3 => "ui_r3",
}
}

Expand Down Expand Up @@ -59,66 +75,135 @@ impl Default for DBusEvent {
}
}

impl From<NativeEvent> for DBusEvent {
/// Convert the [NativeEvent] into a [DBusEvent]
fn from(item: NativeEvent) -> Self {
impl DBusEvent {
/// Convert the [NativeEvent] into one or more [DBusEvent]
pub fn from_native_event(item: NativeEvent) -> Vec<Self> {
let mut events: Vec<Self> = Vec::new();
let input_value = item.get_value();
let value = match input_value {
super::native::InputValue::Bool(value) => {
if value {
1.0
} else {
0.0
}

// Translate the event to dbus action(s)
let actions = actions_from_capability(item.as_capability());

// Create DBus events based on the type of input value
for action in actions {
let event = dbus_event_from_value(action, input_value.clone());
if event.is_none() {
continue;
}
super::native::InputValue::Float(value) => value,
super::native::InputValue::Vector2 { x, y } => 0.0,
super::native::InputValue::Vector3 { x, y, z } => 0.0,
};
events.push(event.unwrap());
}

// Translate the event to an action
let action = match item.as_capability() {
Capability::None => Action::None,
Capability::NotImplemented => Action::None,
Capability::Sync => Action::None,
Capability::Gamepad(gamepad) => match gamepad {
Gamepad::Button(btn) => match btn {
GamepadButton::South => Action::Accept,
GamepadButton::East => Action::Back,
GamepadButton::North => Action::Context,
GamepadButton::West => Action::None,
GamepadButton::Start => Action::Option,
GamepadButton::Select => Action::None,
GamepadButton::Guide => Action::Guide,
GamepadButton::Base => Action::Quick,
GamepadButton::DPadUp => Action::Up,
GamepadButton::DPadDown => Action::Down,
GamepadButton::DPadLeft => Action::Left,
GamepadButton::DPadRight => Action::Right,
GamepadButton::LeftBumper => Action::None,
GamepadButton::LeftTrigger => Action::None,
GamepadButton::LeftPaddle1 => Action::None,
GamepadButton::LeftPaddle2 => Action::None,
GamepadButton::LeftStick => Action::None,
GamepadButton::LeftStickTouch => Action::None,
GamepadButton::LeftTouchpadTouch => Action::None,
GamepadButton::LeftTouchpadPress => Action::None,
GamepadButton::RightBumper => Action::None,
GamepadButton::RightTrigger => Action::None,
GamepadButton::RightPaddle1 => Action::None,
GamepadButton::RightPaddle2 => Action::None,
GamepadButton::RightStick => Action::None,
GamepadButton::RightStickTouch => Action::None,
GamepadButton::RightTouchpadTouch => Action::None,
GamepadButton::RightTouchpadPress => Action::None,
},
Gamepad::Axis(_) => Action::None,
_ => Action::None,
events
}
}

/// Returns an array of DBus event actions from the given event capability.
fn actions_from_capability(capability: Capability) -> Vec<Action> {
match capability {
Capability::None => vec![Action::None],
Capability::NotImplemented => vec![Action::None],
Capability::Sync => vec![Action::None],
Capability::Gamepad(gamepad) => match gamepad {
Gamepad::Button(btn) => match btn {
GamepadButton::South => vec![Action::Accept],
GamepadButton::East => vec![Action::Back],
GamepadButton::North => vec![Action::Context],
GamepadButton::West => vec![Action::ActOn],
GamepadButton::Start => vec![Action::Option],
GamepadButton::Select => vec![Action::Select],
GamepadButton::Guide => vec![Action::Guide],
GamepadButton::Base => vec![Action::Quick],
GamepadButton::DPadUp => vec![Action::Up],
GamepadButton::DPadDown => vec![Action::Down],
GamepadButton::DPadLeft => vec![Action::Left],
GamepadButton::DPadRight => vec![Action::Right],
GamepadButton::LeftBumper => vec![Action::L1],
GamepadButton::LeftTrigger => vec![Action::L2],
GamepadButton::LeftPaddle1 => vec![Action::None],
GamepadButton::LeftPaddle2 => vec![Action::None],
GamepadButton::LeftStick => vec![Action::L3],
GamepadButton::LeftStickTouch => vec![Action::None],
GamepadButton::LeftTouchpadTouch => vec![Action::None],
GamepadButton::LeftTouchpadPress => vec![Action::None],
GamepadButton::RightBumper => vec![Action::R1],
GamepadButton::RightTrigger => vec![Action::R2],
GamepadButton::RightPaddle1 => vec![Action::None],
GamepadButton::RightPaddle2 => vec![Action::None],
GamepadButton::RightStick => vec![Action::R3],
GamepadButton::RightStickTouch => vec![Action::None],
GamepadButton::RightTouchpadTouch => vec![Action::None],
GamepadButton::RightTouchpadPress => vec![Action::None],
},
Capability::Mouse(_) => Action::None,
Capability::Keyboard(_) => Action::None,
};
Gamepad::Axis(axis) => match axis {
GamepadAxis::LeftStick => {
vec![Action::Left, Action::Right, Action::Up, Action::Down]
}
GamepadAxis::Hat1 => {
vec![Action::Left, Action::Right, Action::Up, Action::Down]
}
GamepadAxis::Buttons(negative, positive) => {
let mut dpad_actions = vec![];
// Match negative axis buttons (up and left)
match negative {
GamepadButton::DPadUp => {
dpad_actions.push(Action::Up);
}
GamepadButton::DPadLeft => {
dpad_actions.push(Action::Left);
}
_ => (),
};
// Match positive axis buttons (down and right)
match positive {
GamepadButton::DPadDown => {
dpad_actions.push(Action::Down);
}
GamepadButton::DPadRight => {
dpad_actions.push(Action::Right);
}
_ => (),
}

Self { action, value }
dpad_actions
}
_ => vec![Action::None],
},
_ => vec![Action::None],
},
Capability::Mouse(_) => vec![Action::None],
// TODO: Handle keyboard translation
Capability::Keyboard(_) => vec![Action::None],
}
}

/// Returns a DBus event from the given DBus event action and input value.
fn dbus_event_from_value(action: Action, input_value: InputValue) -> Option<DBusEvent> {
let value = match input_value {
InputValue::Bool(value) => {
if value {
Some(1.0)
} else {
Some(0.0)
}
}
InputValue::Float(value) => Some(value),
InputValue::Vector2 { x, y } => match action {
// Left should be a negative value
Action::Left => x.filter(|&x| x <= 0.0).map(|x| -x),
// Right should be a positive value
Action::Right => x.filter(|&x| x >= 0.0),
// Up should be a negative value
Action::Up => y.filter(|&y| y <= 0.0).map(|y| -y),
// Down should be a positive value
Action::Down => y.filter(|&y| y >= 0.0),
_ => None,
},
InputValue::Vector3 { x, y, z } => None,
};
value?;

Some(DBusEvent {
action,
value: value.unwrap(),
})
}
95 changes: 89 additions & 6 deletions src/input/target/dbus.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::error::Error;

use tokio::sync::{broadcast, mpsc};
use zbus::{fdo, zvariant::ObjectPath, Connection, SignalContext};
use zbus::{fdo, Connection, SignalContext};
use zbus_macros::dbus_interface;

use crate::input::{
Expand All @@ -14,7 +14,19 @@ use crate::input::{

use super::TargetCommand;

/// Size of the channel buffer for events
const BUFFER_SIZE: usize = 2048;
/// The threshold for axis inputs to be considered "pressed"
const AXIS_THRESHOLD: f64 = 0.35;

/// The internal emulated device state for tracking analog input
#[derive(Debug, Clone, Default)]
struct State {
pressed_left: bool,
pressed_right: bool,
pressed_up: bool,
pressed_down: bool,
}

/// The [DBusInterface] provides a DBus interface that can be exposed for managing
/// a [DBusDevice].
Expand Down Expand Up @@ -44,6 +56,7 @@ impl DBusInterface {
/// divert inputs to an overlay over DBus.
#[derive(Debug)]
pub struct DBusDevice {
state: State,
conn: Connection,
dbus_path: Option<String>,
tx: mpsc::Sender<TargetCommand>,
Expand All @@ -56,6 +69,7 @@ impl DBusDevice {
pub fn new(conn: Connection) -> Self {
let (tx, rx) = mpsc::channel(BUFFER_SIZE);
Self {
state: State::default(),
conn,
dbus_path: None,
_composite_tx: None,
Expand Down Expand Up @@ -97,8 +111,10 @@ impl DBusDevice {
match command {
TargetCommand::WriteEvent(event) => {
//log::debug!("Got event to emit: {:?}", event);
let dbus_event = self.translate_event(event);
self.write_dbus_event(dbus_event).await?;
let dbus_events = self.translate_event(event);
for dbus_event in dbus_events {
self.write_dbus_event(dbus_event).await?;
}
}
TargetCommand::Stop => break,
};
Expand All @@ -117,9 +133,76 @@ impl DBusDevice {
Ok(())
}

/// Translate the given native event into a dbus event
fn translate_event(&self, event: NativeEvent) -> DBusEvent {
event.into()
/// Translate the given native event into one or more dbus events
fn translate_event(&mut self, event: NativeEvent) -> Vec<DBusEvent> {
let mut translated = vec![];
let events = DBusEvent::from_native_event(event);
for mut event in events {
// Axis input is a special case, where we need to keep track of the
// current state of the axis, and only emit events whenever the axis
// passes or falls below the defined threshold.
let include_event = match event.action {
Action::Left => {
if self.state.pressed_left && event.value < AXIS_THRESHOLD {
event.value = 0.0;
self.state.pressed_left = false;
true
} else if !self.state.pressed_left && event.value > AXIS_THRESHOLD {
event.value = 1.0;
self.state.pressed_left = true;
true
} else {
false
}
}
Action::Right => {
if self.state.pressed_right && event.value < AXIS_THRESHOLD {
event.value = 0.0;
self.state.pressed_right = false;
true
} else if !self.state.pressed_right && event.value > AXIS_THRESHOLD {
event.value = 1.0;
self.state.pressed_right = true;
true
} else {
false
}
}
Action::Up => {
if self.state.pressed_up && event.value < AXIS_THRESHOLD {
event.value = 0.0;
self.state.pressed_up = false;
true
} else if !self.state.pressed_up && event.value > AXIS_THRESHOLD {
event.value = 1.0;
self.state.pressed_up = true;
true
} else {
false
}
}
Action::Down => {
if self.state.pressed_down && event.value < AXIS_THRESHOLD {
event.value = 0.0;
self.state.pressed_down = false;
true
} else if !self.state.pressed_down && event.value > AXIS_THRESHOLD {
event.value = 1.0;
self.state.pressed_down = true;
true
} else {
false
}
}
_ => true,
};

if include_event {
translated.push(event);
}
}

translated
}

/// Writes the given event to DBus
Expand Down

0 comments on commit 4e3f66b

Please sign in to comment.