Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
787201c3a8 | ||
|
|
6f8a76cb13 | ||
|
|
0f4c567405 | ||
|
|
d9300d80ff | ||
|
|
72ae3121d4 | ||
|
|
75149668ef | ||
|
|
7e4b7b2080 | ||
|
|
28d2a7cca9 | ||
|
|
9a477c48cc | ||
|
|
5598f39315 | ||
|
|
ff2c41cf69 | ||
|
|
21d8cfb185 | ||
|
|
173bf045d9 | ||
|
|
25e99fbf93 | ||
|
|
6bbb9861ef | ||
|
|
645285019e | ||
|
|
49133b5f7a | ||
|
|
26b415667a | ||
|
|
b1cb419f3d | ||
|
|
8182f90783 | ||
|
|
14df9125ef | ||
|
|
451a1c43a3 | ||
|
|
357d35f787 | ||
|
|
de2b4f4da6 | ||
|
|
b710080497 | ||
|
|
e611e56d72 | ||
|
|
3ad12fcd29 |
1044
Cargo.lock
generated
1044
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@@ -8,12 +8,8 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.156", features = ["derive"] }
|
serde = { version = "1.0.156", features = ["derive"] }
|
||||||
serde_yaml = "0.9.19"
|
serde_yaml = "0.9.19"
|
||||||
serde_json = "1"
|
serde_json = { version = "1", default-features=false, features=["alloc"] }
|
||||||
rdkafka = "0.29.0"
|
iced = { version = "0.8.0", default-features=false, features = ["glow"] }
|
||||||
rusb = "0.9.1"
|
|
||||||
hidapi = "0.5.0"
|
|
||||||
gilrs = "0.10.1"
|
|
||||||
iced = "0.8.0"
|
|
||||||
iced_native = "0.9.1"
|
|
||||||
handlebars = "4"
|
handlebars = "4"
|
||||||
iced_futures = { version = "0.6.0", features = ["async-std"] }
|
iced_futures = { version = "0.6.0", default-features=false, features = ["async-std"] }
|
||||||
|
reqwest = { version = "0.11", default-features=false, features = ["blocking"] }
|
||||||
|
|||||||
@@ -37,14 +37,17 @@ pub struct Device {
|
|||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct GUI {
|
pub struct GUI {
|
||||||
pub buttons: Buttons,
|
pub buttons: Buttons,
|
||||||
pub press: PreSufFix,
|
pub release_prefix: String,
|
||||||
pub release: PreSufFix,
|
pub format_keys: Option<String>,
|
||||||
|
pub user: String,
|
||||||
|
pub feedback: GUIFeedback,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct PreSufFix {
|
pub struct GUIFeedback {
|
||||||
pub prefix: String,
|
pub url_read: Option<String>,
|
||||||
pub suffix: String,
|
pub url_say: Option<String>,
|
||||||
|
pub url_send: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -120,13 +123,28 @@ fn build_config_std() -> Config {
|
|||||||
l: env::var("INPUT_GUI_BUTTON_L").unwrap_or(String::from("q")),
|
l: env::var("INPUT_GUI_BUTTON_L").unwrap_or(String::from("q")),
|
||||||
r: env::var("INPUT_GUI_BUTTON_R").unwrap_or(String::from("e")),
|
r: env::var("INPUT_GUI_BUTTON_R").unwrap_or(String::from("e")),
|
||||||
},
|
},
|
||||||
press: PreSufFix{
|
release_prefix: env::var("INPUT_GUI_RELEASE_PREFIX").unwrap_or(String::from("")),
|
||||||
prefix: env::var("INPUT_GUI_PRESS_PREFIX").unwrap_or(String::from("")),
|
user: env::var("INPUT_GUI_USER").unwrap_or(String::from("me")),
|
||||||
suffix: env::var("INPUT_GUI_PRESS_SUFFIX").unwrap_or(String::from("")),
|
format_keys: match env::var("INPUT_GUI_FORMAT") {
|
||||||
|
Ok(x) => Some(x),
|
||||||
|
Err(_) => match env::var("INPUT_GUI_FORMAT_V01").unwrap_or(String::from("false")) == String::from("true") {
|
||||||
|
true => Some(String::from("{\"T\":{{ms}},\"U\":\"{{user}}\",\"Y\":\"{{pressed}}\",\"N\":\"{{released}}\"}")),
|
||||||
|
false => None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
feedback: GUIFeedback{
|
||||||
|
url_read: match env::var("INPUT_GUI_FEEDBACK_URL_READ") {
|
||||||
|
Ok(url) => Some(url),
|
||||||
|
Err(_) => None,
|
||||||
|
},
|
||||||
|
url_say: match env::var("INPUT_GUI_FEEDBACK_URL_SAY") {
|
||||||
|
Ok(url) => Some(url),
|
||||||
|
Err(_) => None,
|
||||||
|
},
|
||||||
|
url_send: match env::var("INPUT_GUI_FEEDBACK_URL_SEND") {
|
||||||
|
Ok(url) => Some(url),
|
||||||
|
Err(_) => None,
|
||||||
},
|
},
|
||||||
release: PreSufFix{
|
|
||||||
prefix: env::var("INPUT_GUI_RELEASE_PREFIX").unwrap_or(String::from("!")),
|
|
||||||
suffix: env::var("INPUT_GUI_RELEASE_SUFFIX").unwrap_or(String::from("")),
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
device: None,
|
device: None,
|
||||||
|
|||||||
347
src/gui.rs
347
src/gui.rs
@@ -1,12 +1,18 @@
|
|||||||
use iced::widget::{button, column, text};
|
use iced::widget::{button, column, row, text};
|
||||||
|
use iced::widget::text_input;
|
||||||
use iced::executor;
|
use iced::executor;
|
||||||
use iced::keyboard;
|
use iced::keyboard;
|
||||||
use iced::subscription;
|
use iced::subscription;
|
||||||
use iced::{Alignment, Element, Application, Settings, Subscription, Theme, Command};
|
use iced::{Alignment, Element, Application, Settings, Subscription, Theme, Command};
|
||||||
use iced_futures::backend::native::async_std::time::every;
|
use iced_futures::backend::native::async_std::time::every;
|
||||||
|
use handlebars::Handlebars;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use std::thread;
|
||||||
|
use reqwest;
|
||||||
|
|
||||||
use crate::stream::OutputStream;
|
use crate::stream::OutputStream;
|
||||||
use crate::config::GUI;
|
use crate::config::{GUI,GUIFeedback};
|
||||||
|
|
||||||
pub fn main(cfg: GUI, output_stream: Box<dyn OutputStream>) -> iced::Result {
|
pub fn main(cfg: GUI, output_stream: Box<dyn OutputStream>) -> iced::Result {
|
||||||
let def: iced::Settings<()> = Settings::default();
|
let def: iced::Settings<()> = Settings::default();
|
||||||
@@ -22,19 +28,28 @@ pub fn main(cfg: GUI, output_stream: Box<dyn OutputStream>) -> iced::Result {
|
|||||||
id: def.id,
|
id: def.id,
|
||||||
text_multithreading: def.text_multithreading,
|
text_multithreading: def.text_multithreading,
|
||||||
try_opengles_first: def.try_opengles_first,
|
try_opengles_first: def.try_opengles_first,
|
||||||
window: def.window,
|
window: iced::window::Settings{
|
||||||
|
size: (300, 720),
|
||||||
|
position: iced::window::Position::Specific(0, 0),
|
||||||
|
..iced::window::Settings::default()
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Main::run(settings)
|
Main::run(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Main {
|
struct Main {
|
||||||
ntfy: String,
|
feedback_recv_c: std::sync::mpsc::Receiver<Feedback>,
|
||||||
|
feedback_send_c: std::sync::mpsc::Sender<Feedback>,
|
||||||
|
ntfy_from_client: String,
|
||||||
|
ntfy_from_server: String,
|
||||||
configuring: Option<Message>,
|
configuring: Option<Message>,
|
||||||
inputs: Inputs,
|
inputs: Inputs,
|
||||||
keys_newly_down: Vec<iced::keyboard::KeyCode>,
|
keys_newly_down: Vec<iced::keyboard::KeyCode>,
|
||||||
keys_already_down: Vec<iced::keyboard::KeyCode>,
|
keys_already_down: Vec<iced::keyboard::KeyCode>,
|
||||||
keys_up: Vec<iced::keyboard::KeyCode>,
|
keys_up: Vec<iced::keyboard::KeyCode>,
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
|
input_text_entry_instruction: String,
|
||||||
|
input_text_entry_value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Flags {
|
struct Flags {
|
||||||
@@ -61,8 +76,11 @@ struct Stick {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Message {
|
enum Message {
|
||||||
EventOccurred(iced_native::Event),
|
EventOccurred(iced::Event),
|
||||||
Tick,
|
Tick,
|
||||||
|
InputTextEntryUpdate(String),
|
||||||
|
InputTextEntrySubmitSay,
|
||||||
|
InputTextEntrySubmitSend,
|
||||||
Up,
|
Up,
|
||||||
Down,
|
Down,
|
||||||
Left,
|
Left,
|
||||||
@@ -75,6 +93,13 @@ enum Message {
|
|||||||
R,
|
R,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum Feedback {
|
||||||
|
Heard(String),
|
||||||
|
Say(String),
|
||||||
|
Send(String),
|
||||||
|
}
|
||||||
|
|
||||||
fn controller_button_to_string(btn: Message, cur: iced::keyboard::KeyCode) -> String {
|
fn controller_button_to_string(btn: Message, cur: iced::keyboard::KeyCode) -> String {
|
||||||
return format!("{:?} => {:?}", cur, btn);
|
return format!("{:?} => {:?}", cur, btn);
|
||||||
}
|
}
|
||||||
@@ -95,6 +120,93 @@ impl Main {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send_from_client(&mut self, text: Feedback) {
|
||||||
|
match text.clone() {
|
||||||
|
Feedback::Say(s) | Feedback::Send(s) if s.len() > 0 && s.len() < 1000 => {},
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
match self.feedback_send_c.send(text) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(err) => eprintln!("main.send_say() failed to enqueue: {}", err),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exchange(&mut self) {
|
||||||
|
self.exchange_send();
|
||||||
|
self.exchange_recv();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exchange_recv(&mut self) {
|
||||||
|
loop {
|
||||||
|
match self.feedback_recv_c.try_recv() {
|
||||||
|
Ok(msg) => {
|
||||||
|
match msg {
|
||||||
|
Feedback::Heard(msg) => self.ntfy_from_server = msg,
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exchange_send(&mut self) {
|
||||||
|
let mut s = vec![];
|
||||||
|
for key_code in self.keys_newly_down.iter() {
|
||||||
|
match self.key_code_to_string(key_code) {
|
||||||
|
Some(x) => {
|
||||||
|
for c in x.chars() {
|
||||||
|
s.push(c);
|
||||||
|
}
|
||||||
|
self.keys_already_down.push(*key_code);
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let mut t = vec![];
|
||||||
|
self.keys_newly_down.clear();
|
||||||
|
for key_code in self.keys_up.iter() {
|
||||||
|
match self.key_code_to_string(key_code) {
|
||||||
|
Some(x) => {
|
||||||
|
for c in x.chars() {
|
||||||
|
for c in self.flags.cfg.release_prefix.chars() {
|
||||||
|
t.push(c);
|
||||||
|
}
|
||||||
|
t.push(c);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if t.len() + s.len() > 0 {
|
||||||
|
self.flags.output_stream.put(self.sprintf_pressed_released(s, t));
|
||||||
|
}
|
||||||
|
self.keys_up.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sprintf(&self, format: &String, content: &Value) -> Vec<char> {
|
||||||
|
return Handlebars::new().render_template(format, content).unwrap().chars().collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sprintf_pressed_released(&self, pressed: Vec<char>, released: Vec<char>) -> Vec<char> {
|
||||||
|
match self.flags.cfg.format_keys.clone() {
|
||||||
|
Some(x) => {
|
||||||
|
return self.sprintf(&x, &json!({
|
||||||
|
"pressed": pressed.iter().collect::<String>(),
|
||||||
|
"released": released.iter().collect::<String>(),
|
||||||
|
"ms": SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis(),
|
||||||
|
"user": self.flags.cfg.user,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
let mut combo = vec![];
|
||||||
|
combo.extend(pressed);
|
||||||
|
combo.extend(released);
|
||||||
|
return combo;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for Main {
|
impl Application for Main {
|
||||||
@@ -104,8 +216,21 @@ impl Application for Main {
|
|||||||
type Executor = executor::Default;
|
type Executor = executor::Default;
|
||||||
|
|
||||||
fn new(flags: Self::Flags) -> (Self, Command<Message>) {
|
fn new(flags: Self::Flags) -> (Self, Command<Message>) {
|
||||||
|
let (sender1, receiver1) = std::sync::mpsc::channel();
|
||||||
|
let (sender2, receiver2) = std::sync::mpsc::channel();
|
||||||
|
let feedback_cfg = flags.cfg.feedback.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
Feedbacker{
|
||||||
|
send_c: sender1,
|
||||||
|
recv_c: receiver2,
|
||||||
|
cfg: feedback_cfg,
|
||||||
|
}.listen()
|
||||||
|
});
|
||||||
return (Self {
|
return (Self {
|
||||||
ntfy: String::from(":wave:"),
|
feedback_send_c: sender2,
|
||||||
|
feedback_recv_c: receiver1,
|
||||||
|
ntfy_from_client: String::from(" "),
|
||||||
|
ntfy_from_server: String::from(" "),
|
||||||
configuring: None,
|
configuring: None,
|
||||||
inputs: Inputs{
|
inputs: Inputs{
|
||||||
stick: Stick {
|
stick: Stick {
|
||||||
@@ -125,6 +250,8 @@ impl Application for Main {
|
|||||||
keys_newly_down: vec![],
|
keys_newly_down: vec![],
|
||||||
keys_already_down: vec![],
|
keys_already_down: vec![],
|
||||||
keys_up: vec![],
|
keys_up: vec![],
|
||||||
|
input_text_entry_instruction: String::from(""),
|
||||||
|
input_text_entry_value: String::from(""),
|
||||||
}, Command::none())
|
}, Command::none())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,50 +262,18 @@ impl Application for Main {
|
|||||||
fn update(&mut self, msg: Message) -> Command<Message> {
|
fn update(&mut self, msg: Message) -> Command<Message> {
|
||||||
match msg.clone() {
|
match msg.clone() {
|
||||||
Message::Tick => {
|
Message::Tick => {
|
||||||
let mut s = vec![];
|
self.exchange();
|
||||||
for key_code in self.keys_newly_down.iter() {
|
|
||||||
match self.key_code_to_string(key_code) {
|
|
||||||
Some(x) => {
|
|
||||||
for c in self.flags.cfg.press.prefix.chars() {
|
|
||||||
s.push(c);
|
|
||||||
}
|
|
||||||
for c in x.chars() {
|
|
||||||
s.push(c);
|
|
||||||
}
|
|
||||||
for c in self.flags.cfg.press.suffix.chars() {
|
|
||||||
s.push(c);
|
|
||||||
}
|
|
||||||
self.keys_already_down.push(*key_code);
|
|
||||||
},
|
},
|
||||||
None => {},
|
Message::InputTextEntryUpdate(payload) => {
|
||||||
};
|
self.input_text_entry_value = payload;
|
||||||
}
|
|
||||||
if s.len() > 0 {
|
|
||||||
self.flags.output_stream.put(s);
|
|
||||||
}
|
|
||||||
self.keys_newly_down.clear();
|
|
||||||
|
|
||||||
let mut s = vec![];
|
|
||||||
for key_code in self.keys_up.iter() {
|
|
||||||
match self.key_code_to_string(key_code) {
|
|
||||||
Some(x) => {
|
|
||||||
for c in x.chars() {
|
|
||||||
for c in self.flags.cfg.release.prefix.chars() {
|
|
||||||
s.push(c);
|
|
||||||
}
|
|
||||||
s.push(c);
|
|
||||||
for c in self.flags.cfg.release.suffix.chars() {
|
|
||||||
s.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None => {},
|
Message::InputTextEntrySubmitSend => {
|
||||||
};
|
self.send_from_client(Feedback::Send(self.input_text_entry_value.clone()));
|
||||||
}
|
self.input_text_entry_value = String::from("");
|
||||||
if s.len() > 0 {
|
},
|
||||||
self.flags.output_stream.put(s);
|
Message::InputTextEntrySubmitSay => {
|
||||||
}
|
self.send_from_client(Feedback::Say(self.input_text_entry_value.clone()));
|
||||||
self.keys_up.clear();
|
self.input_text_entry_value = String::from("");
|
||||||
},
|
},
|
||||||
Message::EventOccurred(event) if self.configuring.is_some() => {
|
Message::EventOccurred(event) if self.configuring.is_some() => {
|
||||||
match event {
|
match event {
|
||||||
@@ -200,11 +295,11 @@ impl Application for Main {
|
|||||||
Message::R => { self.inputs.r = key_code },
|
Message::R => { self.inputs.r = key_code },
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
};
|
||||||
self.ntfy = format!("{:?} => {:?}", key_code.clone(), self.configuring.as_ref().unwrap());
|
self.ntfy_from_client = format!("{:?} => {:?}", key_code.clone(), self.configuring.as_ref().unwrap());
|
||||||
self.configuring = None;
|
self.configuring = None;
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
Message::EventOccurred(event) => {
|
Message::EventOccurred(event) => {
|
||||||
match event {
|
match event {
|
||||||
@@ -220,6 +315,7 @@ impl Application for Main {
|
|||||||
self.keys_newly_down.dedup();
|
self.keys_newly_down.dedup();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
self.exchange();
|
||||||
},
|
},
|
||||||
iced::event::Event::Keyboard(keyboard::Event::KeyReleased{
|
iced::event::Event::Keyboard(keyboard::Event::KeyReleased{
|
||||||
key_code,
|
key_code,
|
||||||
@@ -239,13 +335,14 @@ impl Application for Main {
|
|||||||
},
|
},
|
||||||
None => {},
|
None => {},
|
||||||
};
|
};
|
||||||
|
self.exchange();
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
self.configuring = Some(msg.clone());
|
self.configuring = Some(msg.clone());
|
||||||
self.ntfy = format!("push a key to bind to {:?}", msg.clone());
|
self.ntfy_from_client = format!("push a key to bind to {:?}", msg.clone());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return Command::none();
|
return Command::none();
|
||||||
@@ -253,30 +350,142 @@ impl Application for Main {
|
|||||||
|
|
||||||
fn subscription(&self) -> Subscription<Message> {
|
fn subscription(&self) -> Subscription<Message> {
|
||||||
return subscription::Subscription::batch(vec![
|
return subscription::Subscription::batch(vec![
|
||||||
subscription::events_with(|event, _| match event {
|
subscription::events_with(|event, status| match status {
|
||||||
|
iced::event::Status::Ignored => match event {
|
||||||
iced::Event::Keyboard(_) => Some(Message::EventOccurred(event)),
|
iced::Event::Keyboard(_) => Some(Message::EventOccurred(event)),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
}),
|
}),
|
||||||
every(std::time::Duration::from_millis(5)).map(|_| Message::Tick),
|
every(std::time::Duration::from_millis(5000)).map(|_| Message::Tick),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
|
let new_cfg_button = |msg: Message, s| button(text(controller_button_to_string(msg.clone(), s))).on_press(msg.clone());
|
||||||
return column![
|
return column![
|
||||||
button(text(controller_button_to_string(Message::Up, self.inputs.stick.up))).on_press(Message::Up),
|
text(String::from("= MAYHEM PARTY =")).size(32),
|
||||||
button(text(controller_button_to_string(Message::Down, self.inputs.stick.down))).on_press(Message::Down),
|
column![
|
||||||
button(text(controller_button_to_string(Message::Left, self.inputs.stick.left))).on_press(Message::Left),
|
column![
|
||||||
button(text(controller_button_to_string(Message::Right, self.inputs.stick.right))).on_press(Message::Right),
|
text(String::from("Button Mapping")).size(24),
|
||||||
button(text(controller_button_to_string(Message::A, self.inputs.a))).on_press(Message::A),
|
text(String::from("--------------")).size(24),
|
||||||
button(text(controller_button_to_string(Message::B, self.inputs.b))).on_press(Message::B),
|
new_cfg_button(Message::Up, self.inputs.stick.up),
|
||||||
button(text(controller_button_to_string(Message::X, self.inputs.x))).on_press(Message::X),
|
new_cfg_button(Message::Down, self.inputs.stick.down),
|
||||||
button(text(controller_button_to_string(Message::Y, self.inputs.y))).on_press(Message::Y),
|
new_cfg_button(Message::Left, self.inputs.stick.left),
|
||||||
button(text(controller_button_to_string(Message::L, self.inputs.l))).on_press(Message::L),
|
new_cfg_button(Message::Right, self.inputs.stick.right),
|
||||||
button(text(controller_button_to_string(Message::R, self.inputs.r))).on_press(Message::R),
|
new_cfg_button(Message::A, self.inputs.a),
|
||||||
text(self.ntfy.clone()).size(50),
|
new_cfg_button(Message::B, self.inputs.b),
|
||||||
]
|
new_cfg_button(Message::X, self.inputs.x),
|
||||||
.padding(20)
|
new_cfg_button(Message::Y, self.inputs.y),
|
||||||
.align_items(Alignment::Center)
|
new_cfg_button(Message::L, self.inputs.l),
|
||||||
.into();
|
new_cfg_button(Message::R, self.inputs.r),
|
||||||
|
text(String::from("--------------")).size(24),
|
||||||
|
text(self.ntfy_from_client.clone()).size(18),
|
||||||
|
].padding(20).align_items(Alignment::Center),
|
||||||
|
column![
|
||||||
|
text_input(
|
||||||
|
&self.input_text_entry_instruction,
|
||||||
|
&self.input_text_entry_value,
|
||||||
|
Message::InputTextEntryUpdate
|
||||||
|
),
|
||||||
|
row![
|
||||||
|
button(text("Say")).on_press(Message::InputTextEntrySubmitSay).padding(20),
|
||||||
|
button(text("Send")).on_press(Message::InputTextEntrySubmitSend).padding(20),
|
||||||
|
].padding(20).align_items(Alignment::Center),
|
||||||
|
text(self.ntfy_from_server.clone()).size(18),
|
||||||
|
].padding(20).align_items(Alignment::Center),
|
||||||
|
].padding(0).align_items(Alignment::Center),
|
||||||
|
].padding(0).align_items(Alignment::Center).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Feedbacker {
|
||||||
|
send_c: std::sync::mpsc::Sender<Feedback>,
|
||||||
|
recv_c: std::sync::mpsc::Receiver<Feedback>,
|
||||||
|
cfg: GUIFeedback,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Feedbacker {
|
||||||
|
fn listen(&mut self) {
|
||||||
|
loop {
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||||
|
match self.read_from_server() {
|
||||||
|
Some(msg) if msg.len() > 0 => {
|
||||||
|
self.write_from_server(msg.clone());
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
match self.read_from_client() {
|
||||||
|
Some(msg) => self.write_from_client(msg.clone()),
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_from_server(&mut self) -> Option<String> {
|
||||||
|
return match &self.cfg.url_read {
|
||||||
|
Some(url) => {
|
||||||
|
match reqwest::blocking::get(url) {
|
||||||
|
Ok(resp) => match resp.text() {
|
||||||
|
Ok(text) => Some(text),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("feedback.read: cannot fetch: {}", err);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_from_server(&mut self, msg: String) {
|
||||||
|
match self.send_c.send(Feedback::Heard(msg.clone())) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(err) => eprintln!("feedback.listen() failed to display {}: {}", msg, err),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_from_client(&mut self) -> Option<Feedback> {
|
||||||
|
let mut last: Option<Feedback> = None;
|
||||||
|
loop {
|
||||||
|
match self.recv_c.try_recv() {
|
||||||
|
Ok(msg) => {
|
||||||
|
last = Some(msg);
|
||||||
|
},
|
||||||
|
_ => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_from_client(&mut self, msg: Feedback) {
|
||||||
|
match msg {
|
||||||
|
Feedback::Send(send) if send.len() > 0 => match &self.cfg.url_send {
|
||||||
|
Some(url) => {
|
||||||
|
match reqwest::blocking::get(format!("{}{}", url, send)) {
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("feedback.write_from_client: cannot send: {}", err);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
},
|
||||||
|
Feedback::Say(say) if say.len() > 0 => match &self.cfg.url_say {
|
||||||
|
Some(url) => {
|
||||||
|
match reqwest::blocking::get(format!("{}{}", url, say)) {
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("feedback.write_from_client: cannot say: {}", err);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
use crate::config::Stream;
|
use crate::config::Stream;
|
||||||
|
|
||||||
use hidapi::HidApi;
|
|
||||||
use rusb::UsbContext;
|
|
||||||
use gilrs::{Gilrs, Button, Event};
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use handlebars::Handlebars;
|
use handlebars::Handlebars;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
@@ -20,7 +17,6 @@ pub fn build_input_stream(cfg: &Stream) -> Box<dyn InputStream> {
|
|||||||
return Box::new(build_input_stream_kafka(&cfg).unwrap());
|
return Box::new(build_input_stream_kafka(&cfg).unwrap());
|
||||||
},
|
},
|
||||||
"udp" => return Box::new(build_input_stream_udp(&cfg).unwrap()),
|
"udp" => return Box::new(build_input_stream_udp(&cfg).unwrap()),
|
||||||
"device" => return Box::new(build_input_stream_device(&cfg).unwrap()),
|
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
};
|
||||||
assert!(false);
|
assert!(false);
|
||||||
@@ -30,78 +26,9 @@ pub fn build_input_stream(cfg: &Stream) -> Box<dyn InputStream> {
|
|||||||
pub struct InputStreamDevice {
|
pub struct InputStreamDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_input_stream_device(cfg: &Stream) -> Result<InputStreamDevice, String> {
|
//pub fn build_input_stream_device_gilrs(cfg: &Stream) -> Result<InputStreamDevice, String> {
|
||||||
return build_input_stream_device_gilrs(cfg)
|
//pub fn build_input_stream_device_hidapi(cfg: &Stream) -> Result<InputStreamDevice, String> {
|
||||||
}
|
//pub fn build_input_stream_device_rusb(cfg: &Stream) -> Result<InputStreamDevice, String> {
|
||||||
|
|
||||||
pub fn build_input_stream_device_gilrs(cfg: &Stream) -> Result<InputStreamDevice, String> {
|
|
||||||
let _device_cfg = cfg.engine.device.as_ref().unwrap();
|
|
||||||
|
|
||||||
let mut gilrs = Gilrs::new().unwrap();
|
|
||||||
|
|
||||||
eprintln!("printing gamepads");
|
|
||||||
for (_id, gamepad) in gilrs.gamepads() {
|
|
||||||
eprintln!("{} is {:?}", gamepad.name(), gamepad.power_info());
|
|
||||||
}
|
|
||||||
|
|
||||||
eprintln!("printing gamepads events");
|
|
||||||
loop {
|
|
||||||
// eprintln!("reading gamepads events");
|
|
||||||
// Examine new events
|
|
||||||
while let Some(Event { id, event, time }) = gilrs.next_event() {
|
|
||||||
eprintln!("{:?} New event from {}: {:?}", time, id, event);
|
|
||||||
let active_gamepad = Some(id);
|
|
||||||
|
|
||||||
eprintln!("inspecting event");
|
|
||||||
// You can also use cached gamepad state
|
|
||||||
if let Some(gamepad) = active_gamepad.map(|id| gilrs.gamepad(id)) {
|
|
||||||
if gamepad.is_pressed(Button::South) {
|
|
||||||
eprintln!("Button South is pressed (XBox - A, PS - X)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(15));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Err("do what".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_input_stream_device_hidapi(cfg: &Stream) -> Result<InputStreamDevice, String> {
|
|
||||||
let _device_cfg = cfg.engine.device.as_ref().unwrap();
|
|
||||||
|
|
||||||
match HidApi::new() {
|
|
||||||
Ok(api) => {
|
|
||||||
for device in api.devices() {
|
|
||||||
eprintln!("{:#?}", device);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error: {}", e);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return Err("do what".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_input_stream_device_rusb(cfg: &Stream) -> Result<InputStreamDevice, String> {
|
|
||||||
let _device_cfg = cfg.engine.device.as_ref().unwrap();
|
|
||||||
|
|
||||||
assert!(rusb::has_capability());
|
|
||||||
|
|
||||||
let ctx = rusb::Context::new().unwrap();
|
|
||||||
assert!(ctx.devices().unwrap().len() > 0);
|
|
||||||
|
|
||||||
for device in ctx.devices().unwrap().iter() {
|
|
||||||
let device_desc = device.device_descriptor().unwrap();
|
|
||||||
eprintln!("Bus {:03} Device {:03} ID {:04x}:{:04x}",
|
|
||||||
device.bus_number(),
|
|
||||||
device.address(),
|
|
||||||
device_desc.vendor_id(),
|
|
||||||
device_desc.product_id());
|
|
||||||
}
|
|
||||||
return Err("do what".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputStream for InputStreamDevice {
|
impl InputStream for InputStreamDevice {
|
||||||
fn get(&mut self) -> Vec<char> {
|
fn get(&mut self) -> Vec<char> {
|
||||||
|
|||||||
23
src/testdata/http.go
vendored
Normal file
23
src/testdata/http.go
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
p := os.Getenv("PORT")
|
||||||
|
if err := http.ListenAndServe(":"+p, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
b, _ := io.ReadAll(r.Body)
|
||||||
|
log.Printf("> %s (%+v) %s", r.URL, r.Header, b)
|
||||||
|
body := os.Getenv("BODY")
|
||||||
|
if body == "-" {
|
||||||
|
body = string(b)
|
||||||
|
}
|
||||||
|
w.Write([]byte(body))
|
||||||
|
})); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user