2022-01-30 17:22:55 +08:00
|
|
|
use directories::ProjectDirs;
|
2022-02-07 02:01:34 +08:00
|
|
|
use image::Luma;
|
2022-02-06 17:56:50 +08:00
|
|
|
use log::{info, warn};
|
2022-02-07 02:01:34 +08:00
|
|
|
use qrcode::QrCode;
|
2022-01-28 20:17:34 +08:00
|
|
|
use serde_json::Value;
|
2022-02-05 11:44:42 +08:00
|
|
|
use std::{convert::TryFrom, fs, path::PathBuf};
|
2022-01-28 20:17:34 +08:00
|
|
|
|
2022-02-07 02:01:34 +08:00
|
|
|
use crate::slider_io::utils::list_ips;
|
|
|
|
|
2022-01-28 20:17:34 +08:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum DeviceMode {
|
|
|
|
None,
|
2022-01-29 01:10:12 +08:00
|
|
|
TasollerOne,
|
|
|
|
TasollerTwo,
|
2022-01-28 20:17:34 +08:00
|
|
|
Yuancon,
|
2022-02-07 03:38:48 +08:00
|
|
|
Brokenithm {
|
|
|
|
ground_only: bool,
|
|
|
|
led_enabled: bool,
|
|
|
|
},
|
2022-01-28 20:17:34 +08:00
|
|
|
}
|
|
|
|
|
2022-02-07 02:01:34 +08:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum OutputPolling {
|
|
|
|
Sixty,
|
|
|
|
Hundred,
|
2022-02-07 11:47:11 +08:00
|
|
|
TwoHundredFifty,
|
2022-02-07 02:01:34 +08:00
|
|
|
FiveHundred,
|
|
|
|
Thousand,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OutputPolling {
|
|
|
|
pub fn from_str(s: &str) -> Option<Self> {
|
|
|
|
match s {
|
|
|
|
"60" => Some(OutputPolling::Sixty),
|
|
|
|
"100" => Some(OutputPolling::Hundred),
|
2022-02-07 11:47:11 +08:00
|
|
|
"250" => Some(OutputPolling::TwoHundredFifty),
|
2022-02-07 02:01:34 +08:00
|
|
|
"500" => Some(OutputPolling::FiveHundred),
|
|
|
|
"1000" => Some(OutputPolling::Thousand),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_t_u64(&self) -> u64 {
|
|
|
|
match self {
|
2022-02-07 11:47:11 +08:00
|
|
|
OutputPolling::Sixty => 16666,
|
|
|
|
OutputPolling::Hundred => 10000,
|
|
|
|
OutputPolling::TwoHundredFifty => 4000,
|
|
|
|
OutputPolling::FiveHundred => 2000,
|
|
|
|
OutputPolling::Thousand => 1000,
|
2022-02-07 02:01:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 20:17:34 +08:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum KeyboardLayout {
|
|
|
|
Tasoller,
|
|
|
|
Yuancon,
|
|
|
|
Deemo,
|
2022-02-01 10:37:53 +08:00
|
|
|
Voltex,
|
2022-02-06 17:56:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum GamepadLayout {
|
|
|
|
Voltex,
|
|
|
|
Neardayo,
|
2022-01-28 20:17:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum OutputMode {
|
|
|
|
None,
|
|
|
|
Keyboard {
|
|
|
|
layout: KeyboardLayout,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling,
|
2022-01-28 20:17:34 +08:00
|
|
|
sensitivity: u8,
|
|
|
|
},
|
2022-02-06 17:56:50 +08:00
|
|
|
Gamepad {
|
|
|
|
layout: GamepadLayout,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling,
|
2022-02-06 17:56:50 +08:00
|
|
|
sensitivity: u8,
|
|
|
|
},
|
2022-01-28 20:17:34 +08:00
|
|
|
Websocket {
|
|
|
|
url: String,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling,
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum ReactiveLayout {
|
2022-02-01 10:37:53 +08:00
|
|
|
Even { splits: usize },
|
|
|
|
Voltex,
|
2022-01-28 20:17:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum LedMode {
|
|
|
|
None,
|
2022-01-29 02:51:09 +08:00
|
|
|
Reactive {
|
|
|
|
layout: ReactiveLayout,
|
|
|
|
sensitivity: u8,
|
|
|
|
},
|
2022-01-28 20:17:34 +08:00
|
|
|
Attract,
|
|
|
|
Test,
|
2022-01-29 02:51:09 +08:00
|
|
|
Websocket {
|
|
|
|
url: String,
|
|
|
|
},
|
2022-02-02 15:14:56 +08:00
|
|
|
Serial {
|
|
|
|
port: String,
|
|
|
|
},
|
2022-01-28 20:17:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Config {
|
|
|
|
pub raw: String,
|
|
|
|
pub device_mode: DeviceMode,
|
|
|
|
pub output_mode: OutputMode,
|
|
|
|
pub led_mode: LedMode,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Config {
|
2022-01-29 04:23:16 +08:00
|
|
|
pub fn from_str(s: &str) -> Option<Config> {
|
|
|
|
let v: Value = serde_json::from_str(s).ok()?;
|
2022-01-28 20:17:34 +08:00
|
|
|
|
2022-01-29 04:23:16 +08:00
|
|
|
Some(Config {
|
2022-01-28 20:17:34 +08:00
|
|
|
raw: s.to_string(),
|
2022-01-29 04:23:16 +08:00
|
|
|
device_mode: match v["deviceMode"].as_str()? {
|
2022-01-28 20:17:34 +08:00
|
|
|
"none" => DeviceMode::None,
|
2022-01-29 01:10:12 +08:00
|
|
|
"tasoller-one" => DeviceMode::TasollerOne,
|
|
|
|
"tasoller-two" => DeviceMode::TasollerTwo,
|
2022-01-28 20:17:34 +08:00
|
|
|
"yuancon" => DeviceMode::Yuancon,
|
2022-02-07 03:38:48 +08:00
|
|
|
"brokenithm" => DeviceMode::Brokenithm {
|
|
|
|
ground_only: false,
|
|
|
|
led_enabled: false,
|
|
|
|
},
|
|
|
|
"brokenithm-led" => DeviceMode::Brokenithm {
|
|
|
|
ground_only: false,
|
|
|
|
led_enabled: true,
|
|
|
|
},
|
|
|
|
"brokenithm-ground" => DeviceMode::Brokenithm {
|
|
|
|
ground_only: true,
|
|
|
|
led_enabled: false,
|
|
|
|
},
|
|
|
|
"brokenithm-ground-led" => DeviceMode::Brokenithm {
|
|
|
|
ground_only: true,
|
|
|
|
led_enabled: true,
|
|
|
|
},
|
2022-01-28 20:17:34 +08:00
|
|
|
_ => panic!("Invalid device mode"),
|
|
|
|
},
|
|
|
|
output_mode: match v["outputMode"].as_str().unwrap() {
|
|
|
|
"none" => OutputMode::None,
|
|
|
|
"kb-32-tasoller" => OutputMode::Keyboard {
|
|
|
|
layout: KeyboardLayout::Tasoller,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
2022-01-29 04:23:16 +08:00
|
|
|
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
|
|
|
"kb-32-yuancon" => OutputMode::Keyboard {
|
|
|
|
layout: KeyboardLayout::Yuancon,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
2022-01-29 04:23:16 +08:00
|
|
|
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
2022-02-01 10:37:53 +08:00
|
|
|
"kb-8-deemo" => OutputMode::Keyboard {
|
2022-01-28 20:17:34 +08:00
|
|
|
layout: KeyboardLayout::Deemo,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
2022-01-29 04:23:16 +08:00
|
|
|
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
2022-02-01 10:37:53 +08:00
|
|
|
"kb-voltex" => OutputMode::Keyboard {
|
|
|
|
layout: KeyboardLayout::Voltex,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
2022-02-01 10:37:53 +08:00
|
|
|
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
|
|
|
},
|
2022-02-06 17:56:50 +08:00
|
|
|
"gamepad-voltex" => OutputMode::Gamepad {
|
|
|
|
layout: GamepadLayout::Voltex,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
2022-02-06 17:56:50 +08:00
|
|
|
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
|
|
|
},
|
|
|
|
"gamepad-neardayo" => OutputMode::Gamepad {
|
|
|
|
layout: GamepadLayout::Neardayo,
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
2022-02-02 15:14:56 +08:00
|
|
|
sensitivity: u8::try_from(v["keyboardSensitivity"].as_i64()?).ok()?,
|
|
|
|
},
|
2022-01-28 20:17:34 +08:00
|
|
|
"websocket" => OutputMode::Websocket {
|
2022-02-02 15:14:56 +08:00
|
|
|
url: v["outputWebsocketUrl"].as_str()?.to_string(),
|
2022-02-07 02:01:34 +08:00
|
|
|
polling: OutputPolling::from_str(v["outputPolling"].as_str()?)?,
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
|
|
|
_ => panic!("Invalid output mode"),
|
|
|
|
},
|
2022-01-29 04:23:16 +08:00
|
|
|
led_mode: match v["ledMode"].as_str()? {
|
2022-01-28 20:17:34 +08:00
|
|
|
"none" => LedMode::None,
|
|
|
|
"reactive-4" => LedMode::Reactive {
|
2022-02-01 10:37:53 +08:00
|
|
|
layout: ReactiveLayout::Even { splits: 4 },
|
2022-01-29 04:23:16 +08:00
|
|
|
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
|
|
|
"reactive-8" => LedMode::Reactive {
|
2022-02-01 10:37:53 +08:00
|
|
|
layout: ReactiveLayout::Even { splits: 8 },
|
2022-01-29 04:23:16 +08:00
|
|
|
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
|
|
|
"reactive-16" => LedMode::Reactive {
|
2022-02-01 10:37:53 +08:00
|
|
|
layout: ReactiveLayout::Even { splits: 16 },
|
|
|
|
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
|
|
|
},
|
|
|
|
"reactive-voltex" => LedMode::Reactive {
|
|
|
|
layout: ReactiveLayout::Voltex,
|
2022-01-29 04:23:16 +08:00
|
|
|
sensitivity: u8::try_from(v["ledSensitivity"].as_i64()?).ok()?,
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
|
|
|
"attract" => LedMode::Attract,
|
|
|
|
"test" => LedMode::Test,
|
|
|
|
"websocket" => LedMode::Websocket {
|
2022-02-02 15:14:56 +08:00
|
|
|
url: v["ledWebsocketUrl"].as_str()?.to_string(),
|
|
|
|
},
|
|
|
|
"serial" => LedMode::Serial {
|
|
|
|
port: v["ledSerialPort"].as_str()?.to_string(),
|
2022-01-28 20:17:34 +08:00
|
|
|
},
|
|
|
|
_ => panic!("Invalid led mode"),
|
|
|
|
},
|
2022-01-29 04:23:16 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn factory() -> Self {
|
|
|
|
Self::from_str(
|
|
|
|
r#"{
|
|
|
|
"deviceMode": "none",
|
|
|
|
"outputMode": "none",
|
|
|
|
"ledMode": "none",
|
|
|
|
"keyboardSensitivity": 20,
|
|
|
|
"outputWebsocketUrl": "localhost:3000",
|
2022-02-07 02:01:34 +08:00
|
|
|
"outputPolling": "60",
|
2022-01-29 04:23:16 +08:00
|
|
|
"ledSensitivity": 20,
|
2022-02-02 15:14:56 +08:00
|
|
|
"ledWebsocketUrl": "localhost:3001",
|
|
|
|
"ledSerialPort": "COM5"
|
2022-01-29 04:23:16 +08:00
|
|
|
}"#,
|
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
2022-02-05 18:00:06 +08:00
|
|
|
pub fn get_log_file_path() -> Option<Box<PathBuf>> {
|
2022-02-05 18:07:18 +08:00
|
|
|
let project_dir = ProjectDirs::from("me", "impress labs", "slidershim").unwrap();
|
2022-02-05 18:00:06 +08:00
|
|
|
let config_dir = project_dir.config_dir();
|
|
|
|
fs::create_dir_all(config_dir).unwrap();
|
|
|
|
|
|
|
|
let log_path = config_dir.join("log.txt");
|
|
|
|
|
|
|
|
return Some(Box::new(log_path));
|
|
|
|
}
|
|
|
|
|
2022-02-07 02:01:34 +08:00
|
|
|
pub fn get_brokenithm_qr_path() -> Option<Box<PathBuf>> {
|
|
|
|
let project_dir = ProjectDirs::from("me", "impress labs", "slidershim").unwrap();
|
|
|
|
let config_dir = project_dir.config_dir();
|
|
|
|
fs::create_dir_all(config_dir).unwrap();
|
|
|
|
|
|
|
|
let brokenithm_qr_path = config_dir.join("brokenithm.png");
|
|
|
|
|
|
|
|
let ips = list_ips().ok()?;
|
|
|
|
let link = "http://imp.ress.me/t/sshelper?d=".to_string()
|
|
|
|
+ &ips
|
|
|
|
.into_iter()
|
|
|
|
.filter(|s| s.as_str().chars().filter(|x| *x == '.').count() == 3)
|
|
|
|
.map(|s| base64::encode_config(s, base64::URL_SAFE_NO_PAD))
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.join(";");
|
|
|
|
let qr = QrCode::new(link).ok()?;
|
|
|
|
let image = qr.render::<Luma<u8>>().build();
|
|
|
|
image.save(brokenithm_qr_path.as_path()).ok()?;
|
|
|
|
|
|
|
|
return Some(Box::new(brokenithm_qr_path));
|
|
|
|
}
|
|
|
|
|
2022-01-29 04:23:16 +08:00
|
|
|
fn get_saved_path() -> Option<Box<PathBuf>> {
|
2022-02-05 18:07:18 +08:00
|
|
|
let project_dir = ProjectDirs::from("me", "impress labs", "slidershim").unwrap();
|
2022-01-29 04:23:16 +08:00
|
|
|
let config_dir = project_dir.config_dir();
|
2022-02-05 11:44:42 +08:00
|
|
|
fs::create_dir_all(config_dir).unwrap();
|
2022-01-29 04:23:16 +08:00
|
|
|
|
|
|
|
let config_path = config_dir.join("config.json");
|
|
|
|
|
|
|
|
return Some(Box::new(config_path));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_saved() -> Option<Self> {
|
|
|
|
let config_path = Self::get_saved_path()?;
|
|
|
|
if !config_path.exists() {
|
|
|
|
return None;
|
2022-01-28 20:17:34 +08:00
|
|
|
}
|
2022-01-30 17:22:55 +08:00
|
|
|
info!("Config file found at {:?}", config_path);
|
2022-02-05 11:44:42 +08:00
|
|
|
let saved_data = fs::read_to_string(config_path.as_path()).ok()?;
|
2022-01-29 04:23:16 +08:00
|
|
|
return Self::from_str(saved_data.as_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn default() -> Self {
|
|
|
|
Self::load_saved()
|
2022-02-06 17:56:50 +08:00
|
|
|
.or_else(|| {
|
|
|
|
warn!("Config loading from file failed, using default");
|
|
|
|
Some(Self::factory())
|
|
|
|
})
|
2022-01-29 04:23:16 +08:00
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn save(&self) -> Option<()> {
|
2022-01-30 17:22:55 +08:00
|
|
|
info!("Config saving...");
|
2022-01-29 04:23:16 +08:00
|
|
|
let config_path = Self::get_saved_path()?;
|
2022-01-30 17:22:55 +08:00
|
|
|
info!("Config saving to {:?}", config_path);
|
2022-01-29 04:23:16 +08:00
|
|
|
fs::write(config_path.as_path(), self.raw.as_str()).unwrap();
|
|
|
|
|
2022-01-30 17:22:55 +08:00
|
|
|
info!("Config saved");
|
2022-01-29 04:23:16 +08:00
|
|
|
Some(())
|
2022-01-28 20:17:34 +08:00
|
|
|
}
|
|
|
|
}
|