Initial Commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
292
Cargo.lock
generated
Normal file
292
Cargo.lock
generated
Normal file
@@ -0,0 +1,292 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "deunicode"
|
||||
version = "1.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04"
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "otd-ipc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"dirs",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"slug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slug"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724"
|
||||
dependencies = [
|
||||
"deunicode",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "otd-ipc"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
authors = ["AviiNL <otd-ipc@avii.nl>"]
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.11.1"
|
||||
dirs = "6.0.0"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_bytes = "0.11.19"
|
||||
slug = "0.1.6"
|
||||
68
src/lib.rs
Normal file
68
src/lib.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
mod messages;
|
||||
mod metadata;
|
||||
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
os::unix::net::UnixStream,
|
||||
};
|
||||
|
||||
use crate::{messages::*, metadata::OtdIpcMetadata};
|
||||
|
||||
pub struct OtdIpc {
|
||||
stream: UnixStream,
|
||||
}
|
||||
|
||||
impl OtdIpc {
|
||||
pub fn new(name: &str, version: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let meta = OtdIpcMetadata::new()?;
|
||||
|
||||
let mut stream = UnixStream::connect(meta.socket)?;
|
||||
let hello = Hello::new(
|
||||
0x022026020501,
|
||||
name,
|
||||
version,
|
||||
&format!("{}.{}", slug::slugify(name), meta.id),
|
||||
1,
|
||||
)?;
|
||||
|
||||
stream.write_all(&Vec::from(hello))?;
|
||||
|
||||
Ok(Self { stream })
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for OtdIpc {
|
||||
type Item = Message;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut raw_header = [0; size_of::<Header>()];
|
||||
self.stream.read_exact(&mut raw_header).ok()?;
|
||||
|
||||
let header: Header = raw_header[..].try_into().ok()?;
|
||||
let mut raw_buffer = vec![0; (header.size as usize) - size_of::<Header>()];
|
||||
self.stream.read_exact(&mut raw_buffer).ok()?;
|
||||
|
||||
match header.message_type {
|
||||
messages::MessageType::None => None,
|
||||
messages::MessageType::DeviceInfo => Some(Message::DeviceInfo(Box::new(
|
||||
DeviceInfo::try_from(&raw_buffer[..]).ok()?,
|
||||
))),
|
||||
messages::MessageType::State => {
|
||||
Some(Message::State(State::try_from(&raw_buffer[..]).ok()?))
|
||||
}
|
||||
messages::MessageType::Ping => {
|
||||
Some(Message::Ping(Ping::try_from(&raw_buffer[..]).ok()?))
|
||||
}
|
||||
messages::MessageType::DebugMessage => {
|
||||
Some(Message::DebugMessage(DebugMessage::from(&raw_buffer[..])))
|
||||
}
|
||||
messages::MessageType::Experimental => {
|
||||
Some(Message::Experimental(Experimental::from(&raw_buffer[..])))
|
||||
}
|
||||
messages::MessageType::Hello => {
|
||||
// Some(Message::Hello(Hello::from(&raw_buffer[..])))
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/main.rs
Normal file
11
src/main.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use otd_ipc::OtdIpc;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let otd_ipc = OtdIpc::new("otd-ipc-rs", "master")?;
|
||||
|
||||
for msg in otd_ipc {
|
||||
dbg!(msg);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
16
src/messages/debug_message.rs
Normal file
16
src/messages/debug_message.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DebugMessage {
|
||||
pub message: String, // this can not be known at compile time
|
||||
}
|
||||
|
||||
impl From<&[u8]> for DebugMessage {
|
||||
fn from(value: &[u8]) -> Self {
|
||||
Self {
|
||||
message: String::from_utf8_lossy(value).to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
47
src/messages/device_info.rs
Normal file
47
src/messages/device_info.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::take;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeviceInfo {
|
||||
pub max_x: f32,
|
||||
pub max_y: f32,
|
||||
pub max_pressure: u32,
|
||||
pub persistent_id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for DeviceInfo {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(mut bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 16 + 256 + 256 {
|
||||
return Err("buffer too small");
|
||||
}
|
||||
|
||||
let max_x = f32::from_le_bytes(take::<4>(&mut bytes)?);
|
||||
let max_y = f32::from_le_bytes(take::<4>(&mut bytes)?);
|
||||
let max_pressure = u32::from_le_bytes(take::<4>(&mut bytes)?);
|
||||
|
||||
// skip ignored field
|
||||
let _ = take::<4>(&mut bytes)?;
|
||||
|
||||
let (id_bytes, name_bytes) = bytes.split_at(256);
|
||||
let persistent_id = String::from_utf8_lossy(id_bytes)
|
||||
.trim_end_matches('\0')
|
||||
.to_string();
|
||||
|
||||
let name = String::from_utf8_lossy(name_bytes)
|
||||
.trim_end_matches('\0')
|
||||
.to_string();
|
||||
|
||||
Ok(Self {
|
||||
max_x,
|
||||
max_y,
|
||||
max_pressure,
|
||||
persistent_id,
|
||||
name,
|
||||
})
|
||||
}
|
||||
}
|
||||
16
src/messages/experimental.rs
Normal file
16
src/messages/experimental.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Experimental {
|
||||
// pub sequence_number: u32,
|
||||
}
|
||||
|
||||
impl From<&[u8]> for Experimental {
|
||||
fn from(_value: &[u8]) -> Self {
|
||||
Self {
|
||||
// ..
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/messages/header.rs
Normal file
28
src/messages/header.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::take;
|
||||
|
||||
use super::MessageType;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct Header {
|
||||
pub message_type: MessageType,
|
||||
pub size: u32,
|
||||
pub non_persistent_tablet_id: u32,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Header {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(mut bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 12 {
|
||||
return Err("buffer too small");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
message_type: u32::from_le_bytes(take::<4>(&mut bytes)?).into(),
|
||||
size: u32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
non_persistent_tablet_id: u32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
68
src/messages/hello.rs
Normal file
68
src/messages/hello.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::MessageType;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Hello {
|
||||
pub protocol_version: u64,
|
||||
pub human_readable_name: String,
|
||||
pub human_readable_version: String,
|
||||
pub implementation_id: String,
|
||||
pub compatibility_version: u8,
|
||||
}
|
||||
|
||||
impl Hello {
|
||||
pub fn new(
|
||||
protocol_version: u64,
|
||||
human_readable_name: &str,
|
||||
human_readable_version: &str,
|
||||
implementation_id: &str,
|
||||
compatibility_version: u8,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
Ok(Hello {
|
||||
protocol_version,
|
||||
human_readable_name: human_readable_name.to_string(),
|
||||
human_readable_version: human_readable_version.to_string(),
|
||||
implementation_id: implementation_id.to_string(),
|
||||
compatibility_version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Hello> for Vec<u8> {
|
||||
fn from(value: Hello) -> Self {
|
||||
let mut output = Vec::with_capacity(std::mem::size_of::<Hello>());
|
||||
|
||||
output.extend_from_slice(&(MessageType::Hello as u32).to_le_bytes());
|
||||
output.extend_from_slice(&(std::mem::size_of::<Hello>() as u32).to_le_bytes());
|
||||
|
||||
output.push(0u8); // non_persistent_tablet_id
|
||||
|
||||
output.extend_from_slice(&value.protocol_version.to_le_bytes());
|
||||
|
||||
output.extend_from_slice(&str_to_fixed(&value.human_readable_name).unwrap());
|
||||
output.extend_from_slice(&str_to_fixed(&value.human_readable_version).unwrap());
|
||||
output.extend_from_slice(&str_to_fixed(&value.implementation_id).unwrap());
|
||||
|
||||
output.push(value.compatibility_version);
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
fn str_to_fixed(input: &str) -> Result<[u8; 256], String> {
|
||||
let bytes = input.as_bytes();
|
||||
|
||||
if bytes.len() > 256 {
|
||||
return Err(format!(
|
||||
"String too long ({} bytes), max is 256",
|
||||
bytes.len()
|
||||
));
|
||||
}
|
||||
|
||||
let mut buffer = [0u8; 256];
|
||||
buffer[..bytes.len()].copy_from_slice(bytes);
|
||||
Ok(buffer)
|
||||
}
|
||||
33
src/messages/message_type.rs
Normal file
33
src/messages/message_type.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||
pub enum MessageType {
|
||||
None,
|
||||
DeviceInfo,
|
||||
State,
|
||||
Ping,
|
||||
DebugMessage,
|
||||
Experimental,
|
||||
Hello,
|
||||
}
|
||||
|
||||
impl From<MessageType> for u32 {
|
||||
fn from(value: MessageType) -> Self {
|
||||
value as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for MessageType {
|
||||
fn from(value: u32) -> Self {
|
||||
match value {
|
||||
0 => Self::None,
|
||||
1 => Self::DeviceInfo,
|
||||
2 => Self::State,
|
||||
3 => Self::Ping,
|
||||
4 => Self::DebugMessage,
|
||||
5 => Self::Experimental,
|
||||
6 => Self::Hello,
|
||||
_ => panic!("Invalid value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/messages/mod.rs
Normal file
37
src/messages/mod.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
mod debug_message;
|
||||
mod device_info;
|
||||
mod experimental;
|
||||
mod header;
|
||||
mod hello;
|
||||
mod message_type;
|
||||
mod ping;
|
||||
mod state;
|
||||
|
||||
pub use debug_message::*;
|
||||
pub use device_info::*;
|
||||
pub use experimental::*;
|
||||
pub use header::*;
|
||||
pub use hello::*;
|
||||
pub use message_type::*;
|
||||
pub use ping::*;
|
||||
pub use state::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
DeviceInfo(Box<DeviceInfo>),
|
||||
State(State),
|
||||
Ping(Ping),
|
||||
DebugMessage(DebugMessage),
|
||||
Experimental(Experimental),
|
||||
// Hello(Hello),
|
||||
}
|
||||
|
||||
// helper to read fixed-size chunks
|
||||
pub(super) fn take<const N: usize>(bytes: &mut &[u8]) -> Result<[u8; N], &'static str> {
|
||||
if bytes.len() < N {
|
||||
return Err("buffer too small");
|
||||
}
|
||||
let (head, tail) = bytes.split_at(N);
|
||||
*bytes = tail;
|
||||
Ok(head.try_into().unwrap())
|
||||
}
|
||||
24
src/messages/ping.rs
Normal file
24
src/messages/ping.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::take;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Ping {
|
||||
pub sequence_number: u32,
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Ping {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(mut bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 4 {
|
||||
return Err("buffer too small");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
sequence_number: u32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
145
src/messages/state.rs
Normal file
145
src/messages/state.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::take;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct ValidMask(pub u32);
|
||||
|
||||
impl ValidMask {
|
||||
// pub const NONE: Self = Self(0);
|
||||
pub const POSITION_X: Self = Self(1 << 0);
|
||||
pub const POSITION_Y: Self = Self(1 << 1);
|
||||
pub const PRESSURE: Self = Self(1 << 2);
|
||||
pub const PEN_BUTTONS: Self = Self(1 << 3);
|
||||
pub const AUX_BUTTONS: Self = Self(1 << 4);
|
||||
pub const PEN_IS_NEAR_SURFACE: Self = Self(1 << 5);
|
||||
pub const HOVER_DISTANCE: Self = Self(1 << 6);
|
||||
|
||||
// pub const POSITION: Self = Self(Self::POSITION_X.0 | Self::POSITION_Y.0);
|
||||
|
||||
#[inline]
|
||||
pub fn has(self, mask: ValidMask) -> bool {
|
||||
(self.0 & mask.0) == mask.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for ValidMask {
|
||||
fn from(value: u32) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
// Bitwise operators
|
||||
impl std::ops::BitOr for ValidMask {
|
||||
type Output = Self;
|
||||
fn bitor(self, rhs: Self) -> Self {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitAnd for ValidMask {
|
||||
type Output = Self;
|
||||
fn bitand(self, rhs: Self) -> Self {
|
||||
Self(self.0 & rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Not for ValidMask {
|
||||
type Output = Self;
|
||||
fn not(self) -> Self {
|
||||
Self(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct State {
|
||||
valid_bits: ValidMask,
|
||||
|
||||
x: f32,
|
||||
y: f32,
|
||||
|
||||
pressure: u32,
|
||||
pen_buttons: u32,
|
||||
aux_buttons: u32,
|
||||
hover_distance: u32,
|
||||
pen_is_near_surface: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn x(&self) -> f32 {
|
||||
if !self.valid_bits.has(ValidMask::POSITION_X) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
self.x
|
||||
}
|
||||
|
||||
pub fn y(&self) -> f32 {
|
||||
if !self.valid_bits.has(ValidMask::POSITION_Y) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
self.y
|
||||
}
|
||||
|
||||
pub fn pressure(&self) -> u32 {
|
||||
if !self.valid_bits.has(ValidMask::PRESSURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
self.pressure
|
||||
}
|
||||
pub fn pen_buttons(&self) -> u32 {
|
||||
if !self.valid_bits.has(ValidMask::PEN_BUTTONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
self.pen_buttons
|
||||
}
|
||||
pub fn aux_buttons(&self) -> u32 {
|
||||
if !self.valid_bits.has(ValidMask::AUX_BUTTONS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
self.aux_buttons
|
||||
}
|
||||
pub fn hover_distance(&self) -> u32 {
|
||||
if !self.valid_bits.has(ValidMask::HOVER_DISTANCE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
self.hover_distance
|
||||
}
|
||||
|
||||
pub fn pen_is_near_surface(&self) -> bool {
|
||||
if !self.valid_bits.has(ValidMask::PEN_IS_NEAR_SURFACE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.pen_is_near_surface
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for State {
|
||||
type Error = &'static str;
|
||||
|
||||
fn try_from(mut bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
if bytes.len() < 29 {
|
||||
return Err("buffer too small");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
valid_bits: u32::from_le_bytes(take::<4>(&mut bytes)?).into(),
|
||||
x: f32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
y: f32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
pressure: u32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
pen_buttons: u32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
aux_buttons: u32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
hover_distance: u32::from_le_bytes(take::<4>(&mut bytes)?),
|
||||
pen_is_near_surface: u8::from_le_bytes(take::<1>(&mut bytes)?) != 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
53
src/metadata.rs
Normal file
53
src/metadata.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use std::{collections::HashMap, fs::read_to_string, path::PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
pub(crate) struct OtdIpcMetadata {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub semver: String,
|
||||
pub debug_version: String,
|
||||
pub homepage: String,
|
||||
pub socket: PathBuf,
|
||||
}
|
||||
|
||||
impl OtdIpcMetadata {
|
||||
pub fn new() -> std::io::Result<Self> {
|
||||
let Some(mut otd_ipc_dir) = dirs::data_local_dir() else {
|
||||
panic!("User data local dir should exist!");
|
||||
};
|
||||
|
||||
otd_ipc_dir.push("otd-ipc");
|
||||
otd_ipc_dir.push("servers");
|
||||
otd_ipc_dir.push("v2");
|
||||
|
||||
let mut default = otd_ipc_dir.clone();
|
||||
default.push("default.txt");
|
||||
|
||||
let default_id = read_to_string(default)?;
|
||||
|
||||
let mut available = otd_ipc_dir.clone();
|
||||
available.push("available");
|
||||
available.push(format!("{}.txt", default_id));
|
||||
|
||||
let metadata = read_to_string(available)?;
|
||||
|
||||
let mut data = HashMap::new();
|
||||
|
||||
for line in metadata.lines() {
|
||||
let Some((key, value)) = line.split_once('=') else {
|
||||
continue; // not a valid kvp
|
||||
};
|
||||
data.insert(key, value.to_owned());
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
id: data["ID"].clone(),
|
||||
name: data["NAME"].clone(),
|
||||
semver: data["SEMVER"].clone(),
|
||||
debug_version: data["DEBUG_VERSION"].clone(),
|
||||
homepage: data["HOMEPAGE"].clone(),
|
||||
socket: data["SOCKET"].clone().into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user