Skip to content

Commit

Permalink
controller names
Browse files Browse the repository at this point in the history
wip- I broke so many things
  • Loading branch information
kkevlar committed May 5, 2024
1 parent ad1d4c8 commit fa7baef
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 429 deletions.
22 changes: 17 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mjoy_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ serde_json = "1.0.96"
mjoy_gui = {path = "../mjoy_gui"}
regex = "1.5.4"
rand = "0.8.5"
tracing = "0.1.40"

122 changes: 122 additions & 0 deletions mjoy_core/src/bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use gilrs;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::time::{Duration, Instant};

#[derive(Copy, Clone)]
pub enum UpdateState {
Done,
Binding,
}

pub struct Binder {
bindings_filepath: String,
bindings_to_make: Option<Vec<String>>,
next_binding_allowed_time: Option<Instant>,
cached_state: UpdateState,
}

impl Binder {
pub fn new(bindings_filepath: String) -> Self {
Binder {
bindings_filepath,
bindings_to_make: None,
next_binding_allowed_time: None,
cached_state: UpdateState::Binding,
}
}

pub fn update(
&mut self,
gilrs: &gilrs::Gilrs,
event_path_lookup: &crate::joypaths::EventPathLookup,
mpl: &mut crate::joypaths::MinimalPathLookup,
) -> UpdateState {
// If bindings_to_make is None, read from the filepath
if self.bindings_to_make.is_none() {
let file = File::open(&self.bindings_filepath).expect("Unable to open file");
let reader = BufReader::new(file);
let mut bindings = Vec::new();

for line in reader.lines() {
let line = line.expect("Unable to read line");
bindings.push(line);
}

self.bindings_to_make = Some(bindings);
}

// Check if binding is allowed now
if self.next_binding_allowed_time.is_none()
|| self.next_binding_allowed_time.unwrap() <= Instant::now()
{
if let Some(candidate_binding) = self.bindings_to_make.as_mut().unwrap().last().cloned()
{
match self.perform_candidate_binding(
&candidate_binding,
gilrs,
event_path_lookup,
mpl,
) {
Ok(_) => {
// Binding was successful, remove the candidate from the list and update the time
self.bindings_to_make.as_mut().unwrap().pop();
self.next_binding_allowed_time =
Some(Instant::now() + Duration::from_millis(250));
}
Err(_) => {
// Binding was not successful, do not pop the candidate and do not update the time
// Optionally, you can decide to retry or handle this case differently
}
}
} else {
// No more bindings to process
self.cached_state = UpdateState::Done;
}
}

self.cached_state
}

pub fn perform_candidate_binding(
&mut self,
candidate_binding: &str,
gilrs: &gilrs::Gilrs,
event_path_lookup: &crate::joypaths::EventPathLookup,
mpl: &mut crate::joypaths::MinimalPathLookup,
) -> Result<(), ()> {
for (_id, gamepad) in gilrs.gamepads() {
let button_a = crate::injoy::NamedButton::A;
let button_b = crate::injoy::NamedButton::B;
let button_id_a: gilrs::Button = crate::injoy::snes_namedbutton_to_id(&button_a);
let button_id_b: gilrs::Button = crate::injoy::snes_namedbutton_to_id(&button_b);

// Correctly obtaining button data
let value_a = gamepad
.button_data(button_id_a)
.map_or(0.0, |data| data.value());
let value_b = gamepad
.button_data(button_id_b)
.map_or(0.0, |data| data.value());

// Check if button B is pressed to skip
if value_b > 0.9 {
return Ok(());
}

// Check if button A is pressed to perform the binding
if value_a > 0.9 {
let devpath = gamepad.devpath();

// Correct lookup and mutation process
if let Some(devpath_key) = event_path_lookup.0.get(devpath) {
if let Some(named_path) = mpl.0.get_mut(devpath_key) {
named_path.common_name = Some(candidate_binding.to_string());
return Ok(());
}
}
}
}
Err(())
}
}
159 changes: 93 additions & 66 deletions mjoy_core/src/joypaths.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
use mjoy_gui::wordhash::Wordhash;
use serde::{Deserialize, Serialize};
use serde_json;
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::{BufReader, BufWriter, Write};
use tracing;

#[derive(Debug)]
#[derive(Debug, Deserialize, Serialize)]
pub struct NamedPath {
pub full_path: String,
pub minimal_path: String,
pub root_event_path: String,
pub common_name: String,
pub common_name: Option<String>,
}

pub struct EventPathLookup(pub HashMap<String, NamedPath>);
pub struct EventPathLookup(pub HashMap<String, String>);
impl From<Vec<NamedPath>> for EventPathLookup {
fn from(v: Vec<NamedPath>) -> Self {
let mut m = HashMap::new();
Expand All @@ -24,86 +28,109 @@ pub struct MinimalPathLookup(pub HashMap<String, NamedPath>);
impl From<Vec<NamedPath>> for MinimalPathLookup {
fn from(v: Vec<NamedPath>) -> Self {
let mut m = HashMap::new();

for np in v {
m.insert(np.minimal_path.clone(), np);
}
MinimalPathLookup(m)
}
}

impl MinimalPathLookup {
pub fn read_from_disk(file_path: &str) -> Result<Self, serde_json::Error> {
let file = File::open(file_path).map_err(serde_json::Error::io)?;
let reader = BufReader::new(file);
let named_paths: Vec<NamedPath> = serde_json::from_reader(reader)?;
Ok(MinimalPathLookup::from(named_paths))
}
pub fn write_to_disk(&self, file_path: &str) -> Result<(), serde_json::Error> {
let file = File::create(file_path).map_err(serde_json::Error::io)?;
let writer = BufWriter::new(file);
let named_paths: Vec<&NamedPath> = self.0.values().collect();
serde_json::to_writer_pretty(writer, &named_paths)?;
Ok(())
}
}

#[derive(Debug)]
pub enum RepathError {}

pub fn repath_joys(
words: &Wordhash,
config: &crate::Config,
) -> Result<Vec<NamedPath>, RepathError> {
use regex::Regex;
use std::fs;
impl MinimalPathLookup {
pub fn add_missing_paths_for_joys(
&mut self,
config: &crate::Config,
) -> Result<(), RepathError> {
use regex::Regex;
use std::fs;

let mut joy_paths = Vec::new();
let paths = fs::read_dir("/dev/input/by-path")
.expect("I really should be able to read /dev/input/by-path");

let paths = fs::read_dir("/dev/input/by-path")
.expect("I really should be able to read /dev/input/by-path");
let is_event_joy = Regex::new(r"event-joystick").expect("Compile regex");
let path_only = Regex::new(r"/dev/input/by-path/pci.*usb.*:(.*:1)\.([0-9])-event-joystick")
.expect("compile regex");
let gimme_event = Regex::new(r"../event([0-9]+)").expect("compile regex");

let is_event_joy = Regex::new(r"event-joystick").expect("Compile regex");
let path_only = Regex::new(r"/dev/input/by-path/pci.*usb.*:(.*:1)\.([0-9])-event-joystick")
.expect("compile regex");
let gimme_event = Regex::new(r"../event([0-9]+)").expect("compile regex");
for path in paths {
let path = path.expect("Path conversion failed").path();
let full_path = path.to_str().expect("Path tostring failed");
if is_event_joy.is_match(&full_path) {
let partial_minimal_path = path_only
.captures(&full_path)
.unwrap()
.get(1)
.unwrap()
.as_str()
.to_string();
let multi_controller = path_only
.captures(&full_path)
.unwrap()
.get(2)
.unwrap()
.as_str()
.to_string();
if config.number_of_multi_port_controllers_to_use
<= multi_controller.parse::<u32>().unwrap()
{
continue;
}
let minimal_path = format!("{}.{}", partial_minimal_path, multi_controller);

for path in paths {
let path = path.expect("Path conversion failed").path();
let full_path = path.to_str().expect("Path tostring failed");
if is_event_joy.is_match(&full_path) {
let partial_minimal_path = path_only
.captures(&full_path)
.unwrap()
.get(1)
.unwrap()
.as_str()
.to_string();
let multi_controller = path_only
.captures(&full_path)
.unwrap()
.get(2)
.unwrap()
.as_str()
.to_string();
if config.number_of_multi_port_controllers_to_use
<= multi_controller.parse::<u32>().unwrap()
{
continue;
}
let minimal_path = format!("{}.{}", partial_minimal_path, multi_controller);
let mut minimal_path_bytes = minimal_path.as_bytes().to_vec();
let js_path = std::fs::read_link(&full_path)
.unwrap()
.to_str()
.unwrap()
.to_string();

let common_name =
words.object_name(&mut minimal_path_bytes, config.path_common_name_max_length);
let mut eventpath = "/dev/input/event".to_string();
eventpath.push_str(
gimme_event
.captures(&js_path)
.unwrap()
.get(1)
.unwrap()
.as_str(),
);

let js_path = std::fs::read_link(&full_path)
.unwrap()
.to_str()
.unwrap()
.to_string();
if self.0.contains_key(&minimal_path) {
tracing::info!(
"No need to add {} ({:?}), it's already in our datastructure",
minimal_path,
self.0.get(&minimal_path).unwrap().common_name
);
continue;
}

let mut eventpath = "/dev/input/event".to_string();
eventpath.push_str(
gimme_event
.captures(&js_path)
.unwrap()
.get(1)
.unwrap()
.as_str(),
);
let np = NamedPath {
full_path: full_path.to_owned(),
minimal_path: minimal_path.clone(),
root_event_path: eventpath,
common_name: None,
};

joy_paths.push(NamedPath {
full_path: full_path.to_owned(),
minimal_path,
root_event_path: eventpath,
common_name: common_name.to_owned(),
});
self.0.insert(minimal_path, np);
}
}
Ok(())
}

Ok(joy_paths)
}
Loading

0 comments on commit fa7baef

Please sign in to comment.