Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions example/server.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,14 @@ key = "/etc/rkvm/key.pem"
#
# Change this to your own value before deploying rkvm.
password = "123456789"

# Optional switch to the server (same keys list as switch-keys)
# goto-keys = [ "left-alt", "f1"]

# Client will keep the order (non listed client will be after)
# so switch-keys will cycle server -> 10.10.0.1 -> 10.10.0.2 -> -> other -> server
# [[clients]]
# addr = "10.10.0.1" # identify the client
# goto-keys = [ "left-alt", "f2"] # optional: go to this client directly if it's connected
# [[clients]]
# addr = "10.10.0.2"
13 changes: 12 additions & 1 deletion rkvm-server/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use rkvm_input::key::{Button, Key, Keyboard};
use serde::Deserialize;
use std::collections::HashSet;
use std::net::SocketAddr;
use std::net::{SocketAddr, IpAddr};
use std::path::PathBuf;

#[derive(Deserialize)]
Expand All @@ -13,6 +13,17 @@ pub struct Config {
pub password: String,
pub switch_keys: HashSet<SwitchKey>,
pub propagate_switch_keys: Option<bool>,

pub goto_keys: Option<HashSet<SwitchKey>>,
#[serde(default)]
pub clients: Vec<ClientConfig>,
}

#[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct ClientConfig {
pub addr: IpAddr,
pub goto_keys: Option<HashSet<SwitchKey>>,
}

#[derive(Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
Expand Down
3 changes: 2 additions & 1 deletion rkvm-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ async fn main() -> ExitCode {
};

let switch_keys = config.switch_keys.into_iter().map(Into::into).collect();
let server_goto_keys = config.goto_keys.map(|keys_vec| keys_vec.into_iter().map(Into::into).collect());
let propagate_switch_keys = config.propagate_switch_keys.unwrap_or(true);

tokio::select! {
result = server::run(config.listen, acceptor, &config.password, &switch_keys, propagate_switch_keys) => {
result = server::run(config.listen, acceptor, &config.password, &switch_keys, propagate_switch_keys, &server_goto_keys, &config.clients) => {
if let Err(err) = result {
tracing::error!("Error: {}", err);
return ExitCode::FAILURE;
Expand Down
169 changes: 125 additions & 44 deletions rkvm-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use slab::Slab;
use std::collections::{HashMap, HashSet, VecDeque};
use std::ffi::CString;
use std::io::{self, ErrorKind};
use std::net::SocketAddr;
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
use std::time::Instant;
use thiserror::Error;
use tokio::io::{AsyncWriteExt, BufStream};
Expand All @@ -23,6 +23,10 @@ use tokio::time;
use tokio_rustls::TlsAcceptor;
use tracing::Instrument;

use crate::config::ClientConfig;

const ADDR_UNKNOWN: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED),0);

#[derive(Error, Debug)]
pub enum Error {
#[error("Network error: {0}")]
Expand All @@ -39,17 +43,38 @@ pub async fn run(
password: &str,
switch_keys: &HashSet<Key>,
propagate_switch_keys: bool,
server_goto_keys: &Option<Vec<Key>>,
clients_config: &Vec<ClientConfig>,
) -> Result<(), Error> {
let listener = TcpListener::bind(&listen).await.map_err(Error::Network)?;
tracing::info!("Listening on {}", listen);

let mut monitor = Monitor::new();
let mut devices = Slab::<Device>::new();
let mut clients = Slab::<(Sender<_>, SocketAddr)>::new();
let mut clients = Slab::<Option<(Sender<_>, SocketAddr)>>::new();
let mut current = 0;
let mut previous = 0;
let mut changed = false;
let mut pressed_keys = HashSet::new();
let mut all_switch_keys = switch_keys.clone();
let mut static_client = Vec::new();
let mut goto_keys: HashMap<Vec<Key>,usize> = HashMap::new();


if let Some(keys) = server_goto_keys {
goto_keys.insert(keys.clone(), 0);
all_switch_keys.extend(keys);
}

for c in clients_config {
clients.insert(None);
static_client.push(c.addr);
if let Some(k) = &c.goto_keys {
let keys:Vec<Key> = k.clone().into_iter().map(Into::into).collect();
goto_keys.insert(keys.clone(), static_client.len());
all_switch_keys.extend(keys);
}
}

let (events_sender, mut events_receiver) = mpsc::channel(1);

Expand All @@ -63,8 +88,24 @@ pub async fn run(
let password = password.to_owned();

// Remove dead clients.
clients.retain(|_, (client, _)| !client.is_closed());
if !clients.contains(current) {
for id in 0..static_client.len() {
if let Some((client, _)) = &clients[id] {
if client.is_closed() {
clients[id] = None
}
}
}
clients.retain(|idx, e|
if idx < static_client.len() {
true
} else {
match e {
Some((client, _)) => !client.is_closed(),
None => true,
}
}
);
if current > 0 && (!clients.contains(current) || clients[current].is_none()) {
current = 0;
}

Expand All @@ -85,9 +126,22 @@ pub async fn run(
.collect();

let (sender, receiver) = mpsc::channel(1);
clients.insert((sender, addr));

let span = tracing::info_span!("connection", addr = %addr);
let index = static_client.iter().position(|ip| *ip == addr.ip());
let idx = match index {
Some(idx) => {
if clients[idx].is_some() {
tracing::warn!("client {} already connected", addr);
clients.insert(Some((sender, addr)))
} else {
clients[idx] = Some((sender, addr));
idx
}
},
None => clients.insert(Some((sender, addr))),
};

let span = tracing::info_span!("connection", addr = %addr, idx = %idx);
tokio::spawn(
async move {
tracing::info!("Connected");
Expand All @@ -113,21 +167,26 @@ pub async fn run(
let keys = interceptor.key().collect::<HashSet<_>>();
let repeat = interceptor.repeat();

for (_, (sender, _)) in &clients {
let update = Update::CreateDevice {
id,
name: name.clone(),
version: version.clone(),
vendor: vendor.clone(),
product: product.clone(),
rel: rel.clone(),
abs: abs.clone(),
keys: keys.clone(),
delay: repeat.delay,
period: repeat.period,
};

let _ = sender.send(update).await;
for (_, e) in &clients {
match e {
Some((sender, _)) => {
let update = Update::CreateDevice {
id,
name: name.clone(),
version: version.clone(),
vendor: vendor.clone(),
product: product.clone(),
rel: rel.clone(),
abs: abs.clone(),
keys: keys.clone(),
delay: repeat.delay,
period: repeat.period,
};

let _ = sender.send(update).await;
},
None => {}
}
}

let (interceptor_sender, mut interceptor_receiver) = mpsc::channel(32);
Expand Down Expand Up @@ -189,7 +248,7 @@ pub async fn run(
let mut press = false;

if let Event::Key(KeyEvent { key, down }) = event {
if switch_keys.contains(&key) {
if all_switch_keys.contains(&key) {
press = true;

match down {
Expand All @@ -203,28 +262,41 @@ pub async fn run(
let mut idx = current;

if press {
if pressed_keys.len() == switch_keys.len() {
let exists = |idx| idx == 0 || clients.contains(idx - 1);
loop {
current = (current + 1) % (clients.len() + 1);
if exists(current) {
let exists = |idx| idx == 0 || clients.get(idx - 1).is_some_and(Option::is_some);

// we change in previous event keyup should be send to previous
if changed {
idx = previous;

if pressed_keys.is_empty() {
changed = false
}
} else {
for (keys,&i) in &goto_keys {
if exists(i) && keys.iter().all(|k| pressed_keys.contains(k)) {
current = i;
changed = true;
break;
}
}

previous = idx;
changed = true;
if !changed && switch_keys.is_subset(&pressed_keys) {
loop {
current = (current + 1) % (clients.len() + 1);
if exists(current) {
break;
}
}

if current != 0 {
tracing::info!(idx = %current, addr = %clients[current - 1].1, "Switched client");
} else {
tracing::info!(idx = %current, "Switched client");
changed = true;
}
} else if changed {
idx = previous;

if pressed_keys.is_empty() {
changed = false;
if changed {
previous = idx;
if current != 0 {
tracing::info!(idx = %current, addr = %clients[current - 1].as_ref().map_or_else(|| &ADDR_UNKNOWN, |(_,a)| a), "Switched client");
} else {
tracing::info!(idx = %current, "Switched client");
}
}
}
}
Expand Down Expand Up @@ -254,18 +326,27 @@ pub async fn run(
}

for event in events {
if clients[idx - 1].0.send(Update::Event { id, event }).await.is_err() {
clients.remove(idx - 1);
if let Some((s,_)) = &clients[idx -1] {
if s.send(Update::Event { id, event }).await.is_err() {
if idx - 1 < static_client.len() {
clients[idx -1] = None
} else {
clients.remove(idx - 1);
}

if current == idx {
current = 0;
if current == idx {
current = 0;
}
}
}
}
}
Err(err) if err.kind() == ErrorKind::BrokenPipe => {
for (_, (sender, _)) in &clients {
let _ = sender.send(Update::DestroyDevice { id }).await;
for (_, e) in &clients {
let _ = match e {
Some((sender,_)) => sender.send(Update::DestroyDevice { id }).await,
None => Ok(()),
};
}
devices.remove(id);

Expand Down