From 7e1ec1ed393f0e003f10358011c623852bbf9bbf Mon Sep 17 00:00:00 2001 From: mars Date: Tue, 1 Nov 2022 01:41:13 -0600 Subject: [PATCH] Move magpie_types and magpie_client into magpie --- apps/magpie/Cargo.toml | 26 +++-- apps/magpie/src/client.rs | 41 +++++++ apps/magpie/src/lib.rs | 5 + apps/magpie/src/main.rs | 12 +- apps/magpie/src/protocol.rs | 142 ++++++++++++++++++++++++ apps/magpie/src/{ => service}/gl.rs | 0 apps/magpie/src/{ => service}/ipc.rs | 4 +- apps/magpie/src/service/mod.rs | 3 + apps/magpie/src/{ => service}/window.rs | 4 +- 9 files changed, 219 insertions(+), 18 deletions(-) create mode 100644 apps/magpie/src/client.rs create mode 100644 apps/magpie/src/lib.rs create mode 100644 apps/magpie/src/protocol.rs rename apps/magpie/src/{ => service}/gl.rs (100%) rename apps/magpie/src/{ => service}/ipc.rs (98%) create mode 100644 apps/magpie/src/service/mod.rs rename apps/magpie/src/{ => service}/window.rs (98%) diff --git a/apps/magpie/Cargo.toml b/apps/magpie/Cargo.toml index da94c15..6d09612 100644 --- a/apps/magpie/Cargo.toml +++ b/apps/magpie/Cargo.toml @@ -1,13 +1,23 @@ [package] -name = "magpie" +name = "canary-magpie" version = "0.1.0" edition = "2021" +[[bin]] +name = "magpie" +path = "src/main.rs" +required-features = ["service"] + [dependencies] -canary = { path = "../.." } -glium = "0.32" -magpie-types = { path = "../../crates/magpie-types" } -mio = { version = "0.8", features = ["net", "os-poll"] } -mio-signals = "0.2" -parking_lot = "0.12" -slab = "0.4" +byteorder = "1.4" +canary = { path = "../..", optional = true } +glium = { version = "0.32", optional = true} +mio = { version = "0.8", features = ["net", "os-poll"], optional = true } +mio-signals = { version = "0.2", optional = true } +parking_lot = { version = "0.12", optional = true} +serde = { version = "1", features = ["derive"] } +serde_json = "1" +slab = { version = "0.4", optional = true} + +[features] +service = ["dep:canary", "dep:glium", "dep:mio", "dep:mio-signals", "dep:parking_lot", "dep:slab"] diff --git a/apps/magpie/src/client.rs b/apps/magpie/src/client.rs new file mode 100644 index 0000000..652c7f2 --- /dev/null +++ b/apps/magpie/src/client.rs @@ -0,0 +1,41 @@ +use serde::Serialize; + +use std::os::unix::net::UnixStream; +use std::path::Path; + +use crate::protocol::{ClientMessenger, MagpieServerMsg, PanelId, SendMessage, MAGPIE_SOCK}; + +/// A client to a Magpie server. +pub struct MagpieClient { + pub messenger: ClientMessenger, +} + +impl MagpieClient { + pub fn new() -> std::io::Result { + let sock_dir = std::env::var("XDG_RUNTIME_DIR").expect("XDG_RUNTIME_DIR not set"); + let sock_dir = Path::new(&sock_dir); + let sock_path = sock_dir.join(MAGPIE_SOCK); + let socket = UnixStream::connect(sock_path)?; + Ok(Self { + messenger: ClientMessenger::new(socket), + }) + } + + pub fn send(&mut self, msg: &MagpieServerMsg) { + if let Err(err) = self.messenger.send(msg) { + eprintln!("Message send error: {:?}", err); + } + } + + pub fn send_json_message(&mut self, id: PanelId, msg: &T) { + let msg = serde_json::to_string(msg).unwrap(); + eprintln!("Sending message: {}", msg); + + let msg = SendMessage { + id, + msg: msg.into_bytes(), + }; + + self.send(&MagpieServerMsg::SendMessage(msg)); + } +} diff --git a/apps/magpie/src/lib.rs b/apps/magpie/src/lib.rs new file mode 100644 index 0000000..233623e --- /dev/null +++ b/apps/magpie/src/lib.rs @@ -0,0 +1,5 @@ +pub mod client; +pub mod protocol; + +#[cfg(feature = "service")] +pub mod service; diff --git a/apps/magpie/src/main.rs b/apps/magpie/src/main.rs index 333109f..d53eb7a 100644 --- a/apps/magpie/src/main.rs +++ b/apps/magpie/src/main.rs @@ -1,14 +1,14 @@ use glium::glutin::event_loop::EventLoopBuilder; -pub mod gl; -pub mod ipc; -pub mod window; +use canary_magpie::service::*; +use ipc::Ipc; +use window::{WindowMessage, WindowStore}; fn main() -> std::io::Result<()> { - let event_loop = EventLoopBuilder::::with_user_event().build(); + let event_loop = EventLoopBuilder::::with_user_event().build(); let window_sender = event_loop.create_proxy(); - let (ipc, ipc_sender) = ipc::Ipc::new(window_sender)?; + let (ipc, ipc_sender) = Ipc::new(window_sender)?; let _ipc_thread = std::thread::spawn(|| ipc.run()); - let window_store = window::WindowStore::new(ipc_sender); + let window_store = WindowStore::new(ipc_sender); window_store.run(event_loop) } diff --git a/apps/magpie/src/protocol.rs b/apps/magpie/src/protocol.rs new file mode 100644 index 0000000..a833bc3 --- /dev/null +++ b/apps/magpie/src/protocol.rs @@ -0,0 +1,142 @@ +use std::collections::VecDeque; +use std::io::{Read, Write}; +use std::marker::PhantomData; +use std::path::PathBuf; + +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +/// The name of the Magpie server socket. +pub const MAGPIE_SOCK: &str = "magpie.sock"; + +/// An identifier for a Magpie panel. +/// +/// Only valid on a connection between a single client and its server. Clients +/// are allowed to use arbitrary values for [PanelId]. +pub type PanelId = u32; + +/// Creates a new Magpie panel with a given ID. +/// +/// If the given [PanelId] is already being used on this connection, the server +/// will delete the old panel using that [PanelId]. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct CreatePanel { + pub id: PanelId, + pub script: PathBuf, +} + +/// Sends a panel a message. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct SendMessage { + pub id: PanelId, + pub msg: Vec, +} + +/// A message sent from a Magpie client to the server. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(tag = "kind")] +pub enum MagpieServerMsg { + CreatePanel(CreatePanel), + SendMessage(SendMessage), +} + +/// A message sent from the Magpie server to a client. +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(tag = "kind")] +pub enum MagpieClientMsg {} + +/// A [Messenger] specialized for Magpie clients. +pub type ClientMessenger = Messenger; + +/// A [Messenger] specialized for Magpie servers. +pub type ServerMessenger = Messenger; + +/// Bidirectional, transport-agnostic Magpie IO wrapper struct. +pub struct Messenger { + pub transport: T, + expected_len: Option, + received_buf: VecDeque, + received_queue: VecDeque, + closed: bool, + _output: PhantomData, +} + +impl Messenger { + pub fn new(transport: T) -> Self { + Self { + transport, + expected_len: None, + received_buf: Default::default(), + received_queue: Default::default(), + closed: false, + _output: PhantomData, + } + } + + pub fn is_closed(&self) -> bool { + self.closed + } + + pub fn send(&mut self, msg: &O) -> std::io::Result<()> { + use byteorder::{LittleEndian, WriteBytesExt}; + let payload = serde_json::to_vec(msg).unwrap(); + let len = payload.len() as u32; + self.transport.write_u32::(len)?; + self.transport.write_all(&payload)?; + self.transport.flush()?; + Ok(()) + } + + /// Receives all pending messages and queues them for [recv]. + pub fn flush_recv(&mut self) -> std::io::Result<()> { + let mut buf = [0u8; 1024]; + + loop { + match self.transport.read(&mut buf) { + Ok(0) => { + self.closed = true; + break; + } + Ok(n) => { + self.received_buf.write(&buf[..n])?; + } + Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => break, + Err(ref err) if err.kind() == std::io::ErrorKind::Interrupted => continue, + Err(err) => return Err(err), + } + } + + loop { + if let Some(expected_len) = self.expected_len { + if self.received_buf.len() < expected_len { + break; + } + + self.expected_len = None; + let mut buf = vec![0u8; expected_len]; + self.received_buf.read_exact(&mut buf)?; + match serde_json::from_slice::(&buf) { + Ok(received) => self.received_queue.push_front(received), + Err(e) => { + let kind = std::io::ErrorKind::InvalidData; + let payload = Box::new(e); + let error = std::io::Error::new(kind, payload); + return Err(error); + } + } + } else if self.received_buf.len() >= 4 { + use byteorder::{LittleEndian, ReadBytesExt}; + let expected_len = self.received_buf.read_u32::()?; + self.expected_len = Some(expected_len as usize); + } else { + break; + } + } + + Ok(()) + } + + /// Tries to receive a single input packet. + pub fn recv(&mut self) -> Option { + self.received_queue.pop_back() + } +} diff --git a/apps/magpie/src/gl.rs b/apps/magpie/src/service/gl.rs similarity index 100% rename from apps/magpie/src/gl.rs rename to apps/magpie/src/service/gl.rs diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/service/ipc.rs similarity index 98% rename from apps/magpie/src/ipc.rs rename to apps/magpie/src/service/ipc.rs index 90b004b..07d9273 100644 --- a/apps/magpie/src/ipc.rs +++ b/apps/magpie/src/service/ipc.rs @@ -5,14 +5,14 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; use std::time::Duration; -use magpie_types::{CreatePanel, MagpieServerMsg, SendMessage, ServerMessenger}; use mio::net::{UnixListener, UnixStream}; use mio::{Events, Interest, Poll, Token, Waker}; use mio_signals::{Signal, Signals}; use parking_lot::RwLock; use slab::Slab; -use crate::window::{WindowMessage, WindowMessageSender}; +use crate::service::window::{WindowMessage, WindowMessageSender}; +use crate::protocol::{CreatePanel, MagpieServerMsg, SendMessage, ServerMessenger}; const SOCK_NAME: &str = "magpie.sock"; diff --git a/apps/magpie/src/service/mod.rs b/apps/magpie/src/service/mod.rs new file mode 100644 index 0000000..b57a6fe --- /dev/null +++ b/apps/magpie/src/service/mod.rs @@ -0,0 +1,3 @@ +pub mod gl; +pub mod ipc; +pub mod window; diff --git a/apps/magpie/src/window.rs b/apps/magpie/src/service/window.rs similarity index 98% rename from apps/magpie/src/window.rs rename to apps/magpie/src/service/window.rs index 6572ccf..4fb3aff 100644 --- a/apps/magpie/src/window.rs +++ b/apps/magpie/src/service/window.rs @@ -9,8 +9,8 @@ use glutin::event::{Event, WindowEvent}; use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget}; use glutin::window::WindowId; -use crate::gl::Graphics; -use crate::ipc::{IpcMessage, IpcMessageSender}; +use crate::service::gl::Graphics; +use crate::service::ipc::{IpcMessage, IpcMessageSender}; pub enum WindowMessage { OpenWindow { id: usize, script: PathBuf },