Skip to content

Commit

Permalink
try 1
Browse files Browse the repository at this point in the history
  • Loading branch information
zzeneg committed Oct 2, 2024
1 parent aefbb99 commit c07f13b
Show file tree
Hide file tree
Showing 13 changed files with 300 additions and 219 deletions.
10 changes: 6 additions & 4 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
#[derive(serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
pub device: Device,
pub devices: Vec<Device>,
pub layouts: Vec<String>,
pub reconnect_delay: u64,
}

#[derive(serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Device {
pub name: Option<String>,
pub product_id: u16,
pub usage: u16,
pub usage_page: u16,
}

pub fn get_config() -> Config {
let default_config = Config {
device: Device {
devices: vec![Device {
name: None,
product_id: 0x0844,
usage: 0x61,
usage_page: 0xff60,
},
layouts: vec!["pl".to_string()],
}],
layouts: vec!["en".to_string()],
reconnect_delay: 5000,
};

Expand Down
29 changes: 14 additions & 15 deletions src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use tokio::sync::{broadcast, mpsc};
use crate::config::Device;

pub struct Keyboard {
name: String,
product_id: u16,
usage: u16,
usage_page: u16,
Expand All @@ -13,6 +14,7 @@ pub struct Keyboard {
impl Keyboard {
pub fn new(device: Device, reconnect_delay: u64) -> Self {
return Self {
name: device.name.unwrap_or_else(|| "keyboard".to_string()),
product_id: device.product_id,
usage: device.usage,
usage_page: device.usage_page,
Expand All @@ -33,30 +35,29 @@ impl Keyboard {
return Err(HidError::HidApiErrorEmpty);
}

pub fn connect(&self) -> (broadcast::Sender<bool>, mpsc::Sender<Vec<u8>>) {
pub fn connect(&self, data_sender: broadcast::Sender<Vec<u8>>, is_connected_sender: mpsc::Sender<bool>) {
let name = self.name.clone();
let pid = self.product_id;
let usage = self.usage;
let usage_page = self.usage_page;
let reconnect_delay = self.reconnect_delay;
let (data_sender, mut data_receiver) = mpsc::channel::<Vec<u8>>(32);
let (connected_sender, _) = broadcast::channel::<bool>(32);
let internal_connected_sender = connected_sender.clone();
let is_connected_sender = is_connected_sender.clone();
let mut data_receiver = data_sender.subscribe();
std::thread::spawn(move || {
tracing::info!("Waiting for keyboard...");
tracing::info!("Waiting for {}...", name);
loop {
tracing::debug!("Trying to connect...");
tracing::debug!("Trying to connect to {}...", name);
if let Ok(device) = Self::get_device(&pid, &usage, &usage_page) {
let _ = &internal_connected_sender.send(true).unwrap();
tracing::info!("Connected to keyboard");
let _ = &is_connected_sender.try_send(true).unwrap_or_else(|e| tracing::error!("{}", e));
tracing::info!("Connected to {}", name);
loop {
let msg = data_receiver.blocking_recv();
if let Some(mut received) = msg {
tracing::info!("Sending to keyboard: {:?}", received);
if let Ok(mut received) = data_receiver.blocking_recv() {
tracing::info!("Sending to {}: {:?}", name, received);
received.truncate(32);
received.insert(0, 0);
if let Err(_) = device.write(received.as_mut()) {
let _ = internal_connected_sender.send(false).unwrap();
tracing::warn!("Disconnected from keyboard");
let _ = is_connected_sender.try_send(false).unwrap_or_else(|e| tracing::error!("{}", e));
tracing::warn!("Disconnected from {}", name);

break;
}
Expand All @@ -67,7 +68,5 @@ impl Keyboard {
std::thread::sleep(std::time::Duration::from_millis(reconnect_delay));
}
});

return (connected_sender, data_sender);
}
}
134 changes: 84 additions & 50 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,78 +8,112 @@ mod data_type;
mod keyboard;
mod providers;

use std::thread;
use tokio::sync::{broadcast, mpsc};
use config::get_config;
use keyboard::Keyboard;
#[cfg(not(target_os = "macos"))]
use providers::{_base::Provider, layout::LayoutProvider, time::TimeProvider, media::MediaProvider, volume::VolumeProvider};
use providers::{_base::Provider, layout::LayoutProvider, time::TimeProvider, volume::VolumeProvider};
use std::thread;
use tokio::sync::{broadcast, mpsc};

#[cfg(not(target_os = "macos"))]
use providers::media::MediaProvider;

#[cfg(target_os = "macos")]
use {
providers::{_base::Provider, layout::LayoutProvider, time::TimeProvider, volume::VolumeProvider},
core_foundation_sys::runloop::CFRunLoopRun,
};
use core_foundation_sys::runloop::CFRunLoopRun;

#[cfg(target_os = "macos")]
fn run(layouts: Vec<String>, data_sender: mpsc::Sender<Vec<u8>>, connected_sender: broadcast::Sender<bool>) {
let mut is_connected = false;
let mut connected_receiver = connected_sender.subscribe();
fn main() {
let env_filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(tracing::level_filters::LevelFilter::INFO.into())
.from_env_lossy();
let tracing_subscriber = tracing_subscriber::fmt().with_env_filter(env_filter).finish();
let _ = tracing::subscriber::set_global_default(tracing_subscriber);
let config = get_config();

thread::spawn(move || {
let providers: Vec<Box<dyn Provider>> = vec![
TimeProvider::new(data_sender.clone(), connected_sender.clone()),
LayoutProvider::new(data_sender.clone(), connected_sender.clone(), layouts),
VolumeProvider::new(data_sender.clone(), connected_sender.clone()),
];
let (data_sender, _) = broadcast::channel::<Vec<u8>>(1);
let (is_connected_sender, is_connected_receiver) = mpsc::channel::<bool>(1);

loop {
if let Ok(connected) = connected_receiver.blocking_recv() {
if !is_connected && connected {
providers.iter().for_each(|p| p.start());
}
for device in config.devices {
let data_sender = data_sender.clone();
let is_connected_sender = is_connected_sender.clone();
let reconnect_delay = config.reconnect_delay;
thread::spawn(move || {
let keyboard = Keyboard::new(device, reconnect_delay);
keyboard.connect(data_sender, is_connected_sender);
});
}

is_connected = connected;
}
}
});
unsafe { CFRunLoopRun(); }
run(data_sender, is_connected_receiver);
}

#[cfg(not(target_os = "macos"))]
fn run(layouts: Vec<String>, data_sender: mpsc::Sender<Vec<u8>>, connected_sender: broadcast::Sender<bool>) {
let providers: Vec<Box<dyn Provider>> = vec![
TimeProvider::new(data_sender.clone(), connected_sender.clone()),
VolumeProvider::new(data_sender.clone(), connected_sender.clone()),
LayoutProvider::new(data_sender.clone(), connected_sender.clone(), layouts),
MediaProvider::new(data_sender.clone(), connected_sender.clone()),
fn get_providers(data_sender: &broadcast::Sender<Vec<u8>>) -> Vec<Box<dyn Provider>> {
return vec![
TimeProvider::new(data_sender.clone()),
VolumeProvider::new(data_sender.clone()),
LayoutProvider::new(data_sender.clone()),
MediaProvider::new(data_sender.clone()),
];
}

#[cfg(target_os = "macos")]
fn get_providers(data_sender: &broadcast::Sender<Vec<u8>>, layouts: Vec<String>) -> Vec<Box<dyn Provider>> {
return vec![
TimeProvider::new(data_sender.clone()),
VolumeProvider::new(data_sender.clone()),
LayoutProvider::new(data_sender.clone(), layouts),
];
}

let mut is_connected = false;
let mut connected_receiver = connected_sender.subscribe();
#[cfg(not(target_os = "macos"))]
fn run(data_sender: broadcast::Sender<Vec<u8>>, mut is_connected_receiver: mpsc::Receiver<bool>) {
let providers = get_providers(&data_sender);

let mut connected_count = 0;
let mut is_started = false;

loop {
if let Ok(connected) = connected_receiver.blocking_recv() {
if !is_connected && connected {
if let Some(is_connected) = is_connected_receiver.blocking_recv() {
connected_count += if is_connected { 1 } else { -1 };
tracing::info!("Connected devices: {}", connected_count);

if connected_count > 0 && !is_started {
tracing::info!("Starting providers");
is_started = true;
providers.iter().for_each(|p| p.start());
} else if connected_count == 0 && is_started {
tracing::info!("Stopping providers");
is_started = false;
providers.iter().for_each(|p| p.stop());
}

is_connected = connected;
}
}
}

fn main() {
let env_filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(tracing::level_filters::LevelFilter::INFO.into())
.from_env_lossy();
let tracing_subscriber = tracing_subscriber::fmt().with_env_filter(env_filter).finish();
let _ = tracing::subscriber::set_global_default(tracing_subscriber);
let config = get_config();
#[cfg(target_os = "macos")]
fn run(data_sender: broadcast::Sender<Vec<u8>>, mut is_connected_receiver: mpsc::Receiver<bool>) {
thread::spawn(move || {
let providers = get_providers(&data_sender);

let mut connected_count = 0;
let mut is_started = false;

let keyboard = Keyboard::new(config.device, config.reconnect_delay);
let (connected_sender, data_sender) = keyboard.connect();
loop {
if let Some(is_connected) = is_connected_receiver.blocking_recv() {
connected_count += if is_connected { 1 } else { -1 };
tracing::info!("Connected devices: {}", connected_count);

run(config.layouts, data_sender, connected_sender);
if connected_count > 0 && !is_started {
tracing::info!("Starting providers");
is_started = true;
providers.iter().for_each(|p| p.start());
} else if connected_count == 0 && is_started {
tracing::info!("Stopping providers");
is_started = false;
providers.iter().for_each(|p| p.stop());
}
}
}
});
unsafe {
CFRunLoopRun();
}
}
1 change: 1 addition & 0 deletions src/providers/_base.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub trait Provider {
fn start(&self);
fn stop(&self);
}
33 changes: 19 additions & 14 deletions src/providers/layout/linux.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
use std::sync::Arc;
use std::{ffi, mem, ptr};
use tokio::sync::broadcast;
use x11::xlib::{XGetAtomName, XOpenDisplay, XkbAllocKeyboard, XkbGetNames, XkbGetState, _XDisplay, _XkbDesc, _XkbStateRec};

use crate::config::get_config;
use crate::data_type::DataType;
use tokio::sync::{broadcast, mpsc};
use x11::xlib::{XGetAtomName, XOpenDisplay, XkbAllocKeyboard, XkbGetNames, XkbGetState, _XDisplay, _XkbDesc, _XkbStateRec};

use super::super::_base::Provider;

Expand All @@ -24,27 +27,27 @@ fn get_layout_index(display: *mut _XDisplay) -> usize {
return state.group as usize;
}

fn send_data(value: &String, layouts: &Vec<String>, data_sender: &mpsc::Sender<Vec<u8>>) {
fn send_data(value: &String, layouts: &Vec<String>, data_sender: &broadcast::Sender<Vec<u8>>) {
tracing::info!("new layout: '{0}', layout list: {1:?}", value, layouts);
let index = layouts.into_iter().position(|r| r == value);
if let Some(index) = index {
let data = vec![DataType::Layout as u8, index as u8];
data_sender.try_send(data).unwrap_or_else(|e| tracing::error!("{}", e));
data_sender.send(data).unwrap();
}
}

pub struct LayoutProvider {
data_sender: mpsc::Sender<Vec<u8>>,
connected_sender: broadcast::Sender<bool>,
data_sender: broadcast::Sender<Vec<u8>>,
layouts: Vec<String>,
is_started: Arc<AtomicBool>,
}

impl LayoutProvider {
pub fn new(data_sender: mpsc::Sender<Vec<u8>>, connected_sender: broadcast::Sender<bool>, layouts: Vec<String>) -> Box<dyn Provider> {
pub fn new(data_sender: broadcast::Sender<Vec<u8>>) -> Box<dyn Provider> {
let provider = LayoutProvider {
data_sender,
connected_sender,
layouts,
layouts: get_config().layouts,
is_started: Arc::new(AtomicBool::new(false)),
};
return Box::new(provider);
}
Expand All @@ -53,21 +56,19 @@ impl LayoutProvider {
impl Provider for LayoutProvider {
fn start(&self) {
tracing::info!("Layout Provider started");

self.is_started.store(true, Relaxed);
let data_sender = self.data_sender.clone();
let connected_sender = self.connected_sender.clone();
let layouts = self.layouts.clone();

let is_started = self.is_started.clone();
std::thread::spawn(move || {
let mut connected_receiver = connected_sender.subscribe();
let mut synced_layout = 0;
let display = unsafe { XOpenDisplay(ptr::null()) };
let keyboard = unsafe { XkbAllocKeyboard() };
let symbols = get_symbols(display, keyboard);
let symbol_list = symbols.split('+').map(|x| x.to_string()).collect::<Vec<String>>();

loop {
if !connected_receiver.try_recv().unwrap_or(true) {
if !is_started.load(Relaxed) {
break;
}

Expand All @@ -85,4 +86,8 @@ impl Provider for LayoutProvider {
tracing::info!("Layout Provider stopped");
});
}

fn stop(&self) {
self.is_started.store(false, Relaxed);
}
}
Loading

0 comments on commit c07f13b

Please sign in to comment.