Compare commits

..

No commits in common. "8572d891032af69a63fc082b9c98e5a45853ade1" and "423ef593def0f066d74af2673281e1850dcd5db7" have entirely different histories.

5 changed files with 64 additions and 238 deletions

96
Cargo.lock generated
View file

@ -328,15 +328,6 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.8.1" version = "0.8.1"
@ -894,7 +885,7 @@ dependencies = [
"log 0.3.9", "log 0.3.9",
"mime", "mime",
"num_cpus", "num_cpus",
"time 0.1.45", "time",
"traitobject", "traitobject",
"typeable", "typeable",
"unicase", "unicase",
@ -975,9 +966,7 @@ dependencies = [
"config", "config",
"gilrs", "gilrs",
"gtk4", "gtk4",
"log 0.4.21",
"serde", "serde",
"simplelog",
"toml", "toml",
"websocket", "websocket",
] ]
@ -1209,12 +1198,6 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.16.0" version = "1.16.0"
@ -1225,15 +1208,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "num_threads"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.19.0" version = "1.19.0"
@ -1431,12 +1405,6 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "3.1.0" version = "3.1.0"
@ -1796,17 +1764,6 @@ dependencies = [
"digest 0.10.7", "digest 0.10.7",
] ]
[[package]]
name = "simplelog"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0"
dependencies = [
"log 0.4.21",
"termcolor",
"time 0.3.34",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.9" version = "0.4.9"
@ -1884,15 +1841,6 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.58" version = "1.0.58"
@ -1924,39 +1872,6 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "time"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
dependencies = [
"deranged",
"itoa",
"libc",
"num-conv",
"num_threads",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
dependencies = [
"num-conv",
"time-core",
]
[[package]] [[package]]
name = "tiny-keccak" name = "tiny-keccak"
version = "2.0.2" version = "2.0.2"
@ -2364,15 +2279,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

View file

@ -10,8 +10,6 @@ async-channel = "2.2.0"
config = "0.14.0" config = "0.14.0"
gilrs = "0.10.6" gilrs = "0.10.6"
gtk = { version = "0.8.1", package = "gtk4", features = ["v4_12"] } gtk = { version = "0.8.1", package = "gtk4", features = ["v4_12"] }
log = "0.4.21"
serde = { version = "1.0.197", features = ["derive"] } serde = { version = "1.0.197", features = ["derive"] }
simplelog = "0.12.2"
toml = "0.8.12" toml = "0.8.12"
websocket = "0.27.0" websocket = "0.27.0"

View file

@ -4,74 +4,16 @@ use crate::JoystickThreadUpdate;
use async_channel::{Receiver, Sender}; use async_channel::{Receiver, Sender};
use gilrs::{ev::filter::FilterFn, Axis, Button, Event, EventType, Filter, Gilrs, GilrsBuilder}; use gilrs::{ev::filter::FilterFn, Axis, Button, Event, EventType, Filter, Gilrs, GilrsBuilder};
use std::panic::{self, AssertUnwindSafe};
use std::{ use std::{
sync::{atomic::AtomicBool, Arc}, sync::{atomic::AtomicBool, Arc},
time::{Duration, Instant}, time::{Duration, Instant},
panic::{self, AssertUnwindSafe},
net::TcpStream,
}; };
use websocket::client::{sync::Client, ClientBuilder}; use websocket::client::{sync::Client, ClientBuilder};
use websocket::Message; use websocket::Message;
use log::{info, error};
static MAX_RETRY_ATTEMPTS: u32 = 10;
struct SocketState {
pub ip: String,
pub port: i32,
pub socket: Option<Client<TcpStream>>,
}
impl SocketState {
fn is_connected(&self) -> bool {
self.socket.is_some()
}
fn close_websocket(&mut self) {
if let Some(ref mut x) = self.socket {
info!("closing websocket");
x.send_message(&Message::close()).unwrap();
x.shutdown().unwrap();
self.socket = None;
}
}
fn reconnect_websocket(&mut self) -> bool {
if self.ip.is_empty() {
self.socket = None;
return false;
}
if let Ok(mut val) = ClientBuilder::new(format!("ws://{}:{}", &self.ip, self.port).as_str())
{
if let Ok(val2) = val.connect_insecure() {
self.socket = Some(val2);
true
} else {
error!("couldn't connect websocket! : Step 1");
self.socket = None;
false
}
} else {
error!("couldn't connect websocket! : Step 2");
self.socket = None;
false
}
}
}
struct JTState {
pub socket: SocketState,
pub try_reconnect: bool,
pub retry_attempts: u32,
pub curr_x: i32,
pub curr_y: i32,
pub last_update_time: Instant,
}
struct UnknownSlayer; struct UnknownSlayer;
impl FilterFn for UnknownSlayer { impl FilterFn for UnknownSlayer {
fn filter(&self, ev: Option<Event>, _gilrs: &mut Gilrs) -> Option<Event> { fn filter(&self, ev: Option<Event>, _gilrs: &mut Gilrs) -> Option<Event> {
match ev { match ev {
@ -102,77 +44,69 @@ pub fn joystick_websocket_loop(
) { ) {
let mut gilrs = GilrsBuilder::new().set_update_state(false).build().unwrap(); let mut gilrs = GilrsBuilder::new().set_update_state(false).build().unwrap();
let mut state = JTState { let mut ip: String;
socket: SocketState { let mut port: u32;
ip: String::new(),
port: 0,
socket: None,
},
try_reconnect: false,
retry_attempts: 0,
curr_x: 0, let mut websocket: Option<Client<websocket::stream::sync::TcpStream>> = None;
curr_y: 0,
last_update_time: Instant::now(), let mut curr_x: i32 = 0;
}; let mut curr_y: i32 = 0;
let mut last_update_time = Instant::now();
loop { loop {
match rx.try_recv() { match rx.try_recv() {
Ok(msg) => { Ok(msg) => {
state.socket.ip = msg.ip; ip = msg.ip;
state.socket.port = msg.port as i32; port = msg.port;
save_config(&AppState { save_config(&AppState {
ip: state.socket.ip.clone(), ip: ip.clone(),
port: state.socket.port as u32, port,
}); });
info!("Connecting to: ws://{}:{}", state.socket.ip, state.socket.port); println!("ws://{}:{}", ip, port);
if msg.start_websocket { if let Some(mut x) = websocket {
if !state.socket.is_connected() { println!("closing websocket");
state.socket.reconnect_websocket(); x.send_message(&Message::close()).unwrap();
} x.shutdown().unwrap();
} else if state.socket.is_connected() { websocket = None;
state.socket.close_websocket(); } else {
websocket = {
if let Ok(mut val) =
ClientBuilder::new(format!("ws://{}:{}", ip, port).as_str())
{
if let Ok(val2) = val.connect_insecure() {
Some(val2)
} else {
println!("couldn't connect websocket! : Step 1");
None
}
} else {
println!("couldn't connect websocket! : Step 2");
None
}
};
} }
} }
Err(async_channel::TryRecvError::Closed) => break, Err(async_channel::TryRecvError::Closed) => break,
Err(async_channel::TryRecvError::Empty) => {} Err(async_channel::TryRecvError::Empty) => {}
} }
if state.try_reconnect {
if state.retry_attempts > MAX_RETRY_ATTEMPTS {
state.try_reconnect = false;
}
if state.socket.is_connected() {
state.try_reconnect = false;
} else if state.socket.reconnect_websocket() {
state.try_reconnect = false;
state.retry_attempts = 0;
} else {
state.retry_attempts += 1;
}
}
// catch unwind because some buttons on the joystick will panic the gilrs object
match panic::catch_unwind(AssertUnwindSafe(|| { match panic::catch_unwind(AssertUnwindSafe(|| {
// get the next event, and if it is an axis we are interested in, update the
// corresponding variable
while let Some(evt) = gilrs.next_event().filter_ev(&UnknownSlayer {}, &mut gilrs) { while let Some(evt) = gilrs.next_event().filter_ev(&UnknownSlayer {}, &mut gilrs) {
match evt.event { match evt.event {
gilrs::EventType::AxisChanged(gilrs::Axis::LeftStickY, val, _) => { gilrs::EventType::AxisChanged(gilrs::Axis::LeftStickY, val, _) => {
state.curr_y = (val * 100.0) as i32; curr_y = (val * 100.0) as i32;
if state.curr_y > -10 && state.curr_y < 10 { if curr_y > -10 && curr_y < 10 {
state.curr_y = 0; curr_y = 0;
} }
} }
gilrs::EventType::AxisChanged(gilrs::Axis::LeftStickX, val, _) => { gilrs::EventType::AxisChanged(gilrs::Axis::LeftStickX, val, _) => {
state.curr_x = (val * 100.0) as i32; curr_x = (val * 100.0) as i32;
if state.curr_x > -10 && state.curr_x < 10 { if curr_x > -10 && curr_x < 10 {
state.curr_x = 0; curr_x = 0;
} }
} }
_ => {} _ => {}
@ -181,40 +115,38 @@ pub fn joystick_websocket_loop(
})) { })) {
Ok(_) => {} Ok(_) => {}
Err(_) => { Err(_) => {
info!("panic-causing event captured in gilrs event handler") println!("panic-causing event captured")
} }
} }
if state.socket.is_connected() if websocket.is_some()
&& Instant::now().duration_since(state.last_update_time) >= Duration::from_millis(150) && Instant::now().duration_since(last_update_time) >= Duration::from_millis(150)
{ {
let mut message: String; let mut message: String;
if state.curr_y > 0 { if curr_y > 0 {
message = format!("D{}:", state.curr_y); message = format!("D{}:", curr_y);
} else { } else {
message = format!("U{}:", state.curr_y.abs()); message = format!("U{}:", curr_y.abs());
} }
if state.curr_x > 0 { if curr_x > 0 {
message.push_str(&format!("R{}", state.curr_x)); message.push_str(&format!("R{}", curr_x));
} else { } else {
message.push_str(&format!("L{}", state.curr_x.abs())); message.push_str(&format!("L{}", curr_x.abs()));
} }
if let Some(ref mut websocket_tx) = state.socket.socket { if let Some(mut websocket_tx) = websocket {
if websocket_tx.send_message(&Message::text(message)).is_ok() { websocket_tx.send_message(&Message::text(message)).unwrap();
} else { websocket = Some(websocket_tx);
state.socket.close_websocket();
}
} }
state.last_update_time = Instant::now(); last_update_time = Instant::now();
continue; continue;
} }
match tx.try_send(JoystickThreadUpdate { match tx.try_send(JoystickThreadUpdate {
connected: state.socket.is_connected(), connected: websocket.is_some(),
x_axis: Some(state.curr_x.to_string()), x_axis: Some(curr_x.to_string()),
y_axis: Some(state.curr_y.to_string()), y_axis: Some(curr_y.to_string()),
}) { }) {
Ok(_) => {} Ok(_) => {}
Err(async_channel::TrySendError::Closed(_)) => break, Err(async_channel::TrySendError::Closed(_)) => break,
@ -222,7 +154,7 @@ pub fn joystick_websocket_loop(
} }
if !do_run.load(std::sync::atomic::Ordering::SeqCst) { if !do_run.load(std::sync::atomic::Ordering::SeqCst) {
info!("Exiting joystick thread"); println!("Exiting thread");
break; break;
} }
std::thread::sleep(Duration::from_millis(25)); std::thread::sleep(Duration::from_millis(25));

View file

@ -1,8 +1,6 @@
use gtk::gdk::Display; use gtk::gdk::Display;
use gtk::{glib, Application}; use gtk::{glib, Application};
use gtk::{prelude::*, CssProvider}; use gtk::{prelude::*, CssProvider};
use log::info;
use simplelog::SimpleLogger;
use std::env; use std::env;
mod config; mod config;
@ -19,13 +17,6 @@ pub struct JoystickThreadUpdate {
fn main() -> glib::ExitCode { fn main() -> glib::ExitCode {
env::set_var("gtk_csd", "0"); env::set_var("gtk_csd", "0");
match SimpleLogger::init(simplelog::LevelFilter::Debug, simplelog::Config::default()) {
Ok(_) => {}
Err(e) => {
println!("Failed to init the simplelogger!: {}", e);
}
}
// Create a new application // Create a new application
let app = Application::builder().application_id(APP_ID).build(); let app = Application::builder().application_id(APP_ID).build();
@ -36,7 +27,7 @@ fn main() -> glib::ExitCode {
// Run the application // Run the application
let exit_code = app.run(); let exit_code = app.run();
info!("Closing down"); println!("Closing down");
exit_code exit_code
} }

View file

@ -1,8 +1,8 @@
use gtk::{glib, prelude::*, Box, Entry, Label, ListBox}; use gtk::{glib, prelude::*, Box, Entry, Label, ListBox};
use gtk::{Application, ApplicationWindow, Button}; use gtk::{Application, ApplicationWindow, Button};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::{Arc, atomic::AtomicBool}; use std::sync::atomic::AtomicBool;
use log::error; use std::sync::Arc;
use crate::config::load_config; use crate::config::load_config;
use crate::{joystick_loop, JoystickThreadUpdate}; use crate::{joystick_loop, JoystickThreadUpdate};
@ -10,7 +10,6 @@ use crate::{joystick_loop, JoystickThreadUpdate};
pub struct SocketConnectionUpdate { pub struct SocketConnectionUpdate {
pub ip: String, pub ip: String,
pub port: u32, pub port: u32,
pub start_websocket: bool,
} }
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
@ -93,11 +92,10 @@ pub fn build_ui(app: &Application) {
match tx2.try_send(SocketConnectionUpdate { match tx2.try_send(SocketConnectionUpdate {
ip: ip_text.to_string(), ip: ip_text.to_string(),
port: val, port: val,
start_websocket: ip_entry.get_sensitive(),
}) { }) {
Ok(_) => { } Ok(_) => { }
Err(async_channel::TrySendError::Closed(_)) => {panic!("Joystick thread was closed. Unrecoverable")} Err(async_channel::TrySendError::Closed(_)) => {panic!("Joystick thread was closed. Unrecoverable")}
Err(e) => {error!("There was an error: {e}")} Err(e) => {println!("There was an error: {e}")}
} }
} }
} }
@ -105,6 +103,7 @@ pub fn build_ui(app: &Application) {
glib::spawn_future_local( glib::spawn_future_local(
glib::clone!(@weak axis_label, @weak button, @weak conn_status_label, @weak ip_entry, @weak port_entry, @strong rx => async move { glib::clone!(@weak axis_label, @weak button, @weak conn_status_label, @weak ip_entry, @weak port_entry, @strong rx => async move {
println!("Hello from spawn future local");
while let Ok(msg) = rx.recv().await { while let Ok(msg) = rx.recv().await {
axis_label.set_text( axis_label.set_text(
format!("X: {:>4} Y: {:>4}", msg.x_axis.unwrap_or("0".to_string()), msg.y_axis.unwrap_or("0".to_string())).as_str() format!("X: {:>4} Y: {:>4}", msg.x_axis.unwrap_or("0".to_string()), msg.y_axis.unwrap_or("0".to_string())).as_str()