mirror of
https://github.com/4yn/slidershim.git
synced 2024-11-27 23:10:49 +01:00
keyboard
This commit is contained in:
parent
e097eacdd0
commit
5694d39577
@ -8,8 +8,9 @@ fn main() {
|
||||
let config = Config::from_str(
|
||||
r#"{
|
||||
"deviceMode": "yuancon",
|
||||
"outputMode": "none",
|
||||
"ledMode": "reactive-8"
|
||||
"outputMode": "kb-32-tasoller",
|
||||
"ledMode": "reactive-8",
|
||||
"keyboardSensitivity": 50
|
||||
}"#,
|
||||
);
|
||||
|
||||
|
@ -3,5 +3,6 @@
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
#![feature(div_duration)]
|
||||
#![feature(more_qualified_paths)]
|
||||
|
||||
pub mod slider_io;
|
||||
|
@ -3,6 +3,7 @@
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
#![feature(div_duration)]
|
||||
#![feature(more_qualified_paths)]
|
||||
|
||||
mod slider_io;
|
||||
|
||||
|
@ -69,7 +69,7 @@ impl HidDeviceJob {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_config(mode: &DeviceMode, state: &FullState) -> Self {
|
||||
pub fn from_config(state: &FullState, mode: &DeviceMode) -> Self {
|
||||
match mode {
|
||||
DeviceMode::Yuancon => Self::new(
|
||||
state.clone(),
|
||||
|
@ -90,14 +90,15 @@ impl KeyboardOutput {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, controller_state: &ControllerState, sensitivity: &u8) {
|
||||
pub fn tick(&mut self, flat_controller_state: &Vec<bool>) {
|
||||
self
|
||||
.next_keys
|
||||
.iter_mut()
|
||||
.zip(controller_state.flat(sensitivity))
|
||||
.zip(flat_controller_state)
|
||||
.for_each(|(a, b)| {
|
||||
*a = b;
|
||||
*a = *b;
|
||||
});
|
||||
// println!("{:?}", self.next_keys);
|
||||
self.send();
|
||||
}
|
||||
|
||||
@ -109,7 +110,12 @@ impl KeyboardOutput {
|
||||
fn send(&mut self) {
|
||||
self.n_kb_buf = 0;
|
||||
|
||||
for (i, (n, l)) in self.next_keys.iter().zip(self.last_keys.iter()).enumerate() {
|
||||
for (i, (n, l)) in self
|
||||
.next_keys
|
||||
.iter_mut()
|
||||
.zip(self.last_keys.iter_mut())
|
||||
.enumerate()
|
||||
{
|
||||
match (*n, *l) {
|
||||
(false, true) => {
|
||||
let inner: &mut KEYBDINPUT = unsafe { self.kb_buf[self.n_kb_buf as usize].u.ki_mut() };
|
||||
@ -125,6 +131,7 @@ impl KeyboardOutput {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
*l = *n;
|
||||
}
|
||||
|
||||
if self.n_kb_buf != 0 {
|
||||
|
@ -1,11 +1,6 @@
|
||||
use std::{
|
||||
borrow::BorrowMut,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread::{self, JoinHandle},
|
||||
ops::DerefMut,
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
@ -13,148 +8,114 @@ use palette::{FromColor, Hsv, Srgb};
|
||||
|
||||
use crate::slider_io::{
|
||||
config::{LedMode, ReactiveLayout},
|
||||
controller_state::{ControllerState, FullState, LedState},
|
||||
controller_state::{FullState, LedState},
|
||||
worker::Job,
|
||||
};
|
||||
|
||||
fn update_reactive(
|
||||
controller_state: &ControllerState,
|
||||
led_state: &mut LedState,
|
||||
reactive_layout: &ReactiveLayout,
|
||||
sensitivity: &u8,
|
||||
) {
|
||||
let splits = match reactive_layout {
|
||||
ReactiveLayout::Four => 4,
|
||||
ReactiveLayout::Eight => 8,
|
||||
ReactiveLayout::Sixteen => 16,
|
||||
};
|
||||
let buttons_per_split = 32 / splits;
|
||||
type LedCallback = fn(&Vec<bool>, &mut LedState) -> ();
|
||||
|
||||
let banks: Vec<bool> = controller_state
|
||||
.flat(sensitivity)
|
||||
.chunks(buttons_per_split)
|
||||
.take(splits)
|
||||
.map(|x| x.iter().any(|x| *x))
|
||||
.collect();
|
||||
pub struct LedJob {
|
||||
state: FullState,
|
||||
mode: LedMode,
|
||||
sensitivity: u8,
|
||||
|
||||
// controller_state
|
||||
// .ground_state
|
||||
// .chunks(buttons_per_split)
|
||||
// .map(|x| x.iter().max().unwrap() > &sensitivity)
|
||||
// .collect();
|
||||
splits: usize,
|
||||
buttons_per_split: usize,
|
||||
}
|
||||
|
||||
// (0..splits)
|
||||
// .map(|i| {
|
||||
// controller_state.ground_state[i * buttons_per_split..(i + 1) * buttons_per_split]
|
||||
// .iter()
|
||||
// .max()
|
||||
// .unwrap()
|
||||
// > &sensitivity
|
||||
// })
|
||||
// .collect();
|
||||
|
||||
led_state
|
||||
.led_state
|
||||
.chunks_mut(3)
|
||||
.enumerate()
|
||||
.for_each(|(idx, chunk)| match (idx + 1) % buttons_per_split {
|
||||
0 => {
|
||||
chunk[0] = 255;
|
||||
chunk[1] = 0;
|
||||
chunk[2] = 255;
|
||||
}
|
||||
_ => match banks[idx / buttons_per_split] {
|
||||
true => {
|
||||
chunk[0] = 255;
|
||||
chunk[1] = 0;
|
||||
chunk[2] = 255;
|
||||
}
|
||||
false => {
|
||||
chunk[0] = 255;
|
||||
chunk[1] = 255;
|
||||
chunk[2] = 0;
|
||||
}
|
||||
impl LedJob {
|
||||
pub fn new(state: &FullState, mode: &LedMode) -> Self {
|
||||
let splits = match mode {
|
||||
LedMode::Reactive { layout } => match layout {
|
||||
ReactiveLayout::Four => 4,
|
||||
ReactiveLayout::Eight => 8,
|
||||
ReactiveLayout::Sixteen => 16,
|
||||
},
|
||||
});
|
||||
_ => 16,
|
||||
};
|
||||
|
||||
// println!("{:?}", controller_state.ground_state);
|
||||
// println!("{:?}", banks);
|
||||
// println!("{:?}", led_state.led_state);
|
||||
|
||||
led_state.dirty = true;
|
||||
}
|
||||
|
||||
fn update_attract(led_state: &mut LedState) {
|
||||
let now = Instant::now();
|
||||
let theta = (now - led_state.start).div_duration_f64(Duration::from_secs(4)) % 1.0;
|
||||
|
||||
led_state
|
||||
.led_state
|
||||
.chunks_mut(3)
|
||||
.enumerate()
|
||||
.for_each(|(idx, chunk)| {
|
||||
let slice_theta = (&theta + (idx as f64) / 31.0) % 1.0;
|
||||
let color = Srgb::from_color(Hsv::new(slice_theta * 360.0, 1.0, 1.0)).into_format::<u8>();
|
||||
chunk[0] = color.red;
|
||||
chunk[1] = color.green;
|
||||
chunk[2] = color.blue;
|
||||
});
|
||||
|
||||
// println!("{} {:?}", theta, led_state.led_state);
|
||||
|
||||
led_state.dirty = true;
|
||||
}
|
||||
|
||||
pub struct LedThread {
|
||||
thread: Option<JoinHandle<()>>,
|
||||
stop_signal: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl LedThread {
|
||||
pub fn new(state: &FullState, mode: LedMode) -> Self {
|
||||
let stop_signal = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let stop_signal_clone = Arc::clone(&stop_signal);
|
||||
let controller_state = state.clone_controller();
|
||||
let led_state = state.clone_led();
|
||||
Self {
|
||||
thread: Some(thread::spawn(move || loop {
|
||||
// println!("Led thread: {:?}", mode);
|
||||
match &mode {
|
||||
LedMode::Reactive { layout } => {
|
||||
let controller_state_handle = controller_state.lock().unwrap();
|
||||
let mut led_state_handle = led_state.lock().unwrap();
|
||||
update_reactive(
|
||||
controller_state_handle.deref(),
|
||||
led_state_handle.deref_mut(),
|
||||
layout,
|
||||
&20,
|
||||
)
|
||||
}
|
||||
LedMode::Attract => {
|
||||
let mut led_state_handle = led_state.lock().unwrap();
|
||||
update_attract(led_state_handle.deref_mut());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
state: state.clone(),
|
||||
mode: mode.clone(),
|
||||
sensitivity: 20,
|
||||
|
||||
{
|
||||
if stop_signal_clone.load(Ordering::SeqCst) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(33))
|
||||
})),
|
||||
stop_signal,
|
||||
splits,
|
||||
buttons_per_split: 32 / splits,
|
||||
}
|
||||
}
|
||||
|
||||
fn calc_lights(&self, flat_controller_state: &Vec<bool>, led_state: &mut LedState) {
|
||||
match self.mode {
|
||||
LedMode::Reactive { .. } => {
|
||||
let banks: Vec<bool> = flat_controller_state
|
||||
.chunks(self.buttons_per_split)
|
||||
.take(self.splits)
|
||||
.map(|x| x.iter().any(|x| *x))
|
||||
.collect();
|
||||
|
||||
led_state
|
||||
.led_state
|
||||
.chunks_mut(3)
|
||||
.enumerate()
|
||||
.for_each(|(idx, chunk)| match (idx + 1) % self.buttons_per_split {
|
||||
0 => {
|
||||
chunk[0] = 255;
|
||||
chunk[1] = 0;
|
||||
chunk[2] = 255;
|
||||
}
|
||||
_ => match banks[idx / self.buttons_per_split] {
|
||||
true => {
|
||||
chunk[0] = 255;
|
||||
chunk[1] = 0;
|
||||
chunk[2] = 255;
|
||||
}
|
||||
false => {
|
||||
chunk[0] = 255;
|
||||
chunk[1] = 255;
|
||||
chunk[2] = 0;
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
LedMode::Attract => {
|
||||
let now = Instant::now();
|
||||
let theta = (now - led_state.start).div_duration_f64(Duration::from_secs(4)) % 1.0;
|
||||
led_state
|
||||
.led_state
|
||||
.chunks_mut(3)
|
||||
.enumerate()
|
||||
.for_each(|(idx, chunk)| {
|
||||
let slice_theta = (&theta + (idx as f64) / 31.0) % 1.0;
|
||||
let color =
|
||||
Srgb::from_color(Hsv::new(slice_theta * 360.0, 1.0, 1.0)).into_format::<u8>();
|
||||
chunk[0] = color.red;
|
||||
chunk[1] = color.green;
|
||||
chunk[2] = color.blue;
|
||||
});
|
||||
}
|
||||
_ => panic!("Not implemented"),
|
||||
}
|
||||
|
||||
led_state.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LedThread {
|
||||
fn drop(&mut self) {
|
||||
self.stop_signal.swap(true, Ordering::SeqCst);
|
||||
if self.thread.is_some() {
|
||||
self.thread.take().unwrap().join().ok();
|
||||
impl Job for LedJob {
|
||||
fn setup(&mut self) {}
|
||||
|
||||
fn tick(&mut self) {
|
||||
let flat_controller_state: Vec<bool>;
|
||||
{
|
||||
let controller_state_handle = self.state.controller_state.lock().unwrap();
|
||||
flat_controller_state = controller_state_handle.flat(&self.sensitivity);
|
||||
}
|
||||
|
||||
{
|
||||
let mut led_state_handle = self.state.led_state.lock().unwrap();
|
||||
self.calc_lights(&flat_controller_state, led_state_handle.deref_mut());
|
||||
}
|
||||
thread::sleep(Duration::from_millis(33));
|
||||
}
|
||||
|
||||
fn teardown(&mut self) {}
|
||||
}
|
||||
|
@ -1,30 +1,31 @@
|
||||
use crate::slider_io::{
|
||||
config::Config, controller_state::FullState, device::HidDeviceJob, led::LedThread, worker::Worker,
|
||||
config::Config, controller_state::FullState, device::HidDeviceJob, led::LedJob,
|
||||
output::KeyboardOutputJob, worker::Worker,
|
||||
};
|
||||
|
||||
pub struct Manager {
|
||||
state: FullState,
|
||||
config: Config,
|
||||
// device_thread: DeviceThread,
|
||||
device_worker: Worker,
|
||||
led_thread: LedThread,
|
||||
output_worker: Worker,
|
||||
led_worker: Worker,
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn new(config: Config) -> Self {
|
||||
let state = FullState::new();
|
||||
// let device_thread = DeviceThread::new(&state, config.device_mode.clone());
|
||||
let device_worker = Worker::new(HidDeviceJob::from_config(&config.device_mode, &state));
|
||||
let led_thread = LedThread::new(&state, config.led_mode.clone());
|
||||
let device_worker = Worker::new(HidDeviceJob::from_config(&state, &config.device_mode));
|
||||
let output_worker = Worker::new(KeyboardOutputJob::new(&state, &config.output_mode));
|
||||
let led_worker = Worker::new(LedJob::new(&state, &config.led_mode));
|
||||
|
||||
println!("Starting manager with config: {:?}", config);
|
||||
|
||||
Self {
|
||||
state,
|
||||
config,
|
||||
// device_thread,
|
||||
device_worker,
|
||||
led_thread,
|
||||
output_worker,
|
||||
led_worker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
pub mod config;
|
||||
pub mod controller_state;
|
||||
pub mod device;
|
||||
pub mod keyboard;
|
||||
pub mod led;
|
||||
pub mod manager;
|
||||
pub mod output;
|
||||
pub mod worker;
|
||||
|
||||
pub mod keyboard;
|
||||
|
||||
pub mod device;
|
||||
pub mod led;
|
||||
pub mod output;
|
||||
|
||||
pub mod manager;
|
||||
|
@ -1,64 +1,52 @@
|
||||
use std::{
|
||||
ops::Deref,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
thread::{self, JoinHandle},
|
||||
time::Duration,
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::slider_io::{config::OutputMode, controller_state::FullState, keyboard};
|
||||
use crate::slider_io::{
|
||||
config::{KeyboardLayout, OutputMode},
|
||||
controller_state::FullState,
|
||||
keyboard::KeyboardOutput,
|
||||
worker::Job,
|
||||
};
|
||||
|
||||
pub struct OutputThread {
|
||||
thread: Option<JoinHandle<()>>,
|
||||
stop_signal: Arc<AtomicBool>,
|
||||
pub struct KeyboardOutputJob {
|
||||
state: FullState,
|
||||
sensitivity: u8,
|
||||
keyboard_output: KeyboardOutput,
|
||||
}
|
||||
|
||||
impl OutputThread {
|
||||
pub fn new(state: &FullState, mode: OutputMode) -> Self {
|
||||
let controller_state = state.clone_controller();
|
||||
let stop_signal = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let stop_signal_clone = Arc::clone(&stop_signal);
|
||||
|
||||
Self {
|
||||
thread: Some(match mode {
|
||||
OutputMode::None => thread::spawn(|| {}),
|
||||
OutputMode::Keyboard {
|
||||
layout,
|
||||
sensitivity,
|
||||
} => thread::spawn(move || {
|
||||
let mut keyboard_output = keyboard::KeyboardOutput::new(layout);
|
||||
loop {
|
||||
{
|
||||
let controller_state_handle = controller_state.lock().unwrap();
|
||||
keyboard_output.tick(controller_state_handle.deref(), &sensitivity);
|
||||
}
|
||||
|
||||
{
|
||||
if stop_signal_clone.load(Ordering::SeqCst) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
|
||||
keyboard_output.reset();
|
||||
}),
|
||||
OutputMode::Websocket { .. } => thread::spawn(|| {}),
|
||||
}),
|
||||
stop_signal,
|
||||
impl KeyboardOutputJob {
|
||||
pub fn new(state: &FullState, mode: &OutputMode) -> Self {
|
||||
match mode {
|
||||
OutputMode::Keyboard {
|
||||
layout,
|
||||
sensitivity,
|
||||
} => Self {
|
||||
state: state.clone(),
|
||||
sensitivity: *sensitivity,
|
||||
keyboard_output: KeyboardOutput::new(layout.clone()),
|
||||
},
|
||||
_ => panic!("Not implemented"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OutputThread {
|
||||
fn drop(&mut self) {
|
||||
self.stop_signal.swap(true, Ordering::SeqCst);
|
||||
if self.thread.is_some() {
|
||||
self.thread.take().unwrap().join().ok();
|
||||
impl Job for KeyboardOutputJob {
|
||||
fn setup(&mut self) {}
|
||||
|
||||
fn tick(&mut self) {
|
||||
let flat_controller_state: Vec<bool>;
|
||||
{
|
||||
let controller_state_handle = self.state.controller_state.lock().unwrap();
|
||||
flat_controller_state = controller_state_handle.flat(&self.sensitivity);
|
||||
}
|
||||
|
||||
self.keyboard_output.tick(&flat_controller_state);
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
|
||||
fn teardown(&mut self) {
|
||||
self.keyboard_output.reset();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user