From 825f4b28c897619b0c6eee6292233bec4d8bf767 Mon Sep 17 00:00:00 2001 From: mars Date: Thu, 27 Oct 2022 18:12:12 -0600 Subject: [PATCH 01/13] Initial Magpie infrastructure --- Cargo.toml | 2 + apps/magpie/Cargo.toml | 8 +++ apps/magpie/src/main.rs | 89 ++++++++++++++++++++++++++++++++++ crates/magpie-types/Cargo.toml | 7 +++ crates/magpie-types/src/lib.rs | 1 + 5 files changed, 107 insertions(+) create mode 100644 apps/magpie/Cargo.toml create mode 100644 apps/magpie/src/main.rs create mode 100644 crates/magpie-types/Cargo.toml create mode 100644 crates/magpie-types/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index ceaaf59..c1a0013 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,9 @@ [workspace] members = [ + "apps/magpie", "apps/music-player", "apps/sandbox", + "crates/magpie-types", "crates/script", "crates/types", "scripts/music-player", diff --git a/apps/magpie/Cargo.toml b/apps/magpie/Cargo.toml new file mode 100644 index 0000000..03a7b37 --- /dev/null +++ b/apps/magpie/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "magpie" +version = "0.1.0" +edition = "2021" + +[dependencies] +mio = { version = "0.8", features = ["net", "os-poll"] } +mio-signals = "0.2" diff --git a/apps/magpie/src/main.rs b/apps/magpie/src/main.rs new file mode 100644 index 0000000..e767693 --- /dev/null +++ b/apps/magpie/src/main.rs @@ -0,0 +1,89 @@ +use mio::net::UnixListener; +use mio::{Events, Interest, Poll, Token}; +use mio_signals::{Signal, Signals}; +use std::ops::{Deref, DerefMut}; +use std::path::{Path, PathBuf}; +use std::time::Duration; + +const SOCK_NAME: &str = "magpie.sock"; + +/// Wraps [mio::net::UnixListener] with automatic file deletion on drop. +pub struct Listener { + pub uds: UnixListener, + pub path: PathBuf, +} + +impl Drop for Listener { + fn drop(&mut self) { + match std::fs::remove_file(&self.path) { + Ok(_) => {} + Err(e) => eprintln!("Could not delete UnixListener {:?}", e), + } + } +} + +impl Deref for Listener { + type Target = UnixListener; + + fn deref(&self) -> &UnixListener { + &self.uds + } +} + +impl DerefMut for Listener { + fn deref_mut(&mut self) -> &mut UnixListener { + &mut self.uds + } +} + +fn main() -> 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(SOCK_NAME); + eprintln!("Making socket at: {:?}", sock_path); + + let mut listener = Listener { + uds: UnixListener::bind(&sock_path)?, + path: sock_path.to_path_buf(), + }; + + let mut signals = Signals::new(Signal::Interrupt | Signal::Quit)?; + + let mut poll = Poll::new()?; + let mut events = Events::with_capacity(128); + + const LISTENER: Token = Token(0); + poll.registry() + .register(&mut listener.uds, LISTENER, Interest::READABLE)?; + + const SIGNALS: Token = Token(1); + poll.registry() + .register(&mut signals, SIGNALS, Interest::READABLE)?; + + let mut quit = false; + while !quit { + let wait = Duration::from_millis(100); + poll.poll(&mut events, Some(wait))?; + + for event in events.iter() { + match event.token() { + LISTENER => loop { + match listener.accept() { + Ok((connection, address)) => { + println!("Got a connection from: {:?}", address); + } + Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => break, + Err(err) => return Err(err), + } + }, + SIGNALS => while let Some(received) = signals.receive()? { + eprintln!("Received {:?} signal; exiting...", received); + quit = true; + } + _ => panic!("Unrecognized event token: {:?}", event), + } + } + } + + Ok(()) +} diff --git a/crates/magpie-types/Cargo.toml b/crates/magpie-types/Cargo.toml new file mode 100644 index 0000000..12f347a --- /dev/null +++ b/crates/magpie-types/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "magpie-types" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } diff --git a/crates/magpie-types/src/lib.rs b/crates/magpie-types/src/lib.rs new file mode 100644 index 0000000..5befe6a --- /dev/null +++ b/crates/magpie-types/src/lib.rs @@ -0,0 +1 @@ +// protocol types go here From 97c5a76227adc52bc34787624c9d69c9d1d59ff3 Mon Sep 17 00:00:00 2001 From: mars Date: Thu, 27 Oct 2022 20:15:47 -0600 Subject: [PATCH 02/13] Refactor Magpie modules --- apps/magpie/Cargo.toml | 3 + apps/magpie/src/gl.rs | 58 +++++++++++++++++ apps/magpie/src/ipc.rs | 127 ++++++++++++++++++++++++++++++++++++++ apps/magpie/src/main.rs | 90 ++------------------------- apps/magpie/src/window.rs | 12 ++++ 5 files changed, 205 insertions(+), 85 deletions(-) create mode 100644 apps/magpie/src/gl.rs create mode 100644 apps/magpie/src/ipc.rs create mode 100644 apps/magpie/src/window.rs diff --git a/apps/magpie/Cargo.toml b/apps/magpie/Cargo.toml index 03a7b37..1f43cdb 100644 --- a/apps/magpie/Cargo.toml +++ b/apps/magpie/Cargo.toml @@ -4,5 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +canary = { path = "../.." } +glium = "0.32" mio = { version = "0.8", features = ["net", "os-poll"] } mio-signals = "0.2" +slab = "0.4" diff --git a/apps/magpie/src/gl.rs b/apps/magpie/src/gl.rs new file mode 100644 index 0000000..c1ea4cb --- /dev/null +++ b/apps/magpie/src/gl.rs @@ -0,0 +1,58 @@ +use glium::{glutin, Display, Surface}; + +#[derive(Copy, Clone)] +pub struct Vertex { + pub position: [f32; 2], + pub color: [u8; 4], +} + +glium::implement_vertex!(Vertex, position normalize(false), color normalize(true)); + +impl From<&canary::MeshVertex> for Vertex { + fn from(v: &canary::MeshVertex) -> Self { + let (r, g, b, a) = v.color.to_rgba_unmultiplied(); + Self { + position: [v.position.x, v.position.y], + color: [r, g, b, a], + } + } +} + +const VERTEX_SHADER_SRC: &str = r#" + #version 330 + + in vec2 position; + in vec4 color; + + out vec4 frag_color; + + void main() { + gl_Position = vec4(position, 0.0, 1.0); + frag_color = color; + } +"#; + +const FRAGMENT_SHADER_SRC: &str = r#" + #version 330 + + in vec4 frag_color; + + out vec4 fb_color; + + void main() { + fb_color = frag_color; + } +"#; + +pub struct Graphics { + pub program: glium::Program, +} + +impl Graphics { + pub fn new(display: &glium::Display) -> Self { + let program = + glium::Program::from_source(display, VERTEX_SHADER_SRC, FRAGMENT_SHADER_SRC, None) + .unwrap(); + Self { program } + } +} diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/ipc.rs new file mode 100644 index 0000000..3cb1257 --- /dev/null +++ b/apps/magpie/src/ipc.rs @@ -0,0 +1,127 @@ +use mio::net::UnixListener; +use mio::{Events, Interest, Poll, Token}; +use mio_signals::{Signal, Signals}; +use slab::Slab; +use std::ops::{Deref, DerefMut}; +use std::path::{Path, PathBuf}; +use std::time::Duration; + +const SOCK_NAME: &str = "magpie.sock"; + +/// Wraps [mio::net::UnixListener] with automatic file deletion on drop. +pub struct Listener { + pub uds: UnixListener, + pub path: PathBuf, +} + +impl Drop for Listener { + fn drop(&mut self) { + match std::fs::remove_file(&self.path) { + Ok(_) => {} + Err(e) => eprintln!("Could not delete UnixListener {:?}", e), + } + } +} + +impl Deref for Listener { + type Target = UnixListener; + + fn deref(&self) -> &UnixListener { + &self.uds + } +} + +impl DerefMut for Listener { + fn deref_mut(&mut self) -> &mut UnixListener { + &mut self.uds + } +} + +pub struct Client {} + +pub struct Ipc { + pub poll: Poll, + pub events: Events, + pub quit: bool, + pub listener: Listener, + pub signals: Signals, + pub listener_token: Token, + pub signals_token: Token, + pub clients: Slab, +} + +impl Ipc { + 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(SOCK_NAME); + eprintln!("Making socket at: {:?}", sock_path); + + let mut listener = Listener { + uds: UnixListener::bind(&sock_path)?, + path: sock_path.to_path_buf(), + }; + + let mut signals = Signals::new(Signal::Interrupt | Signal::Quit)?; + + let events = Events::with_capacity(128); + let poll = Poll::new()?; + let listener_token = Token(usize::MAX); + let signals_token = Token(listener_token.0 - 1); + + let registry = poll.registry(); + let interest = Interest::READABLE; + registry.register(&mut listener.uds, listener_token, interest)?; + registry.register(&mut signals, signals_token, interest)?; + + Ok(Self { + poll, + events, + quit: false, + listener, + signals, + listener_token, + signals_token, + clients: Default::default(), + }) + } + + pub fn poll(&mut self, timeout: Option) -> std::io::Result<()> { + self.poll.poll(&mut self.events, timeout)?; + + for event in self.events.iter() { + if event.token() == self.listener_token { + loop { + match self.listener.accept() { + Ok((connection, address)) => { + println!("Got a connection from: {:?}", address); + } + Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => break, + Err(err) => return Err(err), + } + } + } else if event.token() == self.signals_token { + while let Some(received) = self.signals.receive()? { + eprintln!("Received {:?} signal; exiting...", received); + self.quit = true; + } + } else { + panic!("Unrecognized event token: {:?}", event); + } + } + + Ok(()) + } + + pub fn run(mut self) { + while !self.quit { + let wait = Duration::from_millis(100); + match self.poll(Some(wait)) { + Ok(_) => {} + Err(e) => { + eprintln!("IPC poll error: {:?}", e); + } + } + } + } +} diff --git a/apps/magpie/src/main.rs b/apps/magpie/src/main.rs index e767693..c5a09e0 100644 --- a/apps/magpie/src/main.rs +++ b/apps/magpie/src/main.rs @@ -1,89 +1,9 @@ -use mio::net::UnixListener; -use mio::{Events, Interest, Poll, Token}; -use mio_signals::{Signal, Signals}; -use std::ops::{Deref, DerefMut}; -use std::path::{Path, PathBuf}; -use std::time::Duration; - -const SOCK_NAME: &str = "magpie.sock"; - -/// Wraps [mio::net::UnixListener] with automatic file deletion on drop. -pub struct Listener { - pub uds: UnixListener, - pub path: PathBuf, -} - -impl Drop for Listener { - fn drop(&mut self) { - match std::fs::remove_file(&self.path) { - Ok(_) => {} - Err(e) => eprintln!("Could not delete UnixListener {:?}", e), - } - } -} - -impl Deref for Listener { - type Target = UnixListener; - - fn deref(&self) -> &UnixListener { - &self.uds - } -} - -impl DerefMut for Listener { - fn deref_mut(&mut self) -> &mut UnixListener { - &mut self.uds - } -} +pub mod gl; +pub mod ipc; +pub mod window; fn main() -> 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(SOCK_NAME); - eprintln!("Making socket at: {:?}", sock_path); - - let mut listener = Listener { - uds: UnixListener::bind(&sock_path)?, - path: sock_path.to_path_buf(), - }; - - let mut signals = Signals::new(Signal::Interrupt | Signal::Quit)?; - - let mut poll = Poll::new()?; - let mut events = Events::with_capacity(128); - - const LISTENER: Token = Token(0); - poll.registry() - .register(&mut listener.uds, LISTENER, Interest::READABLE)?; - - const SIGNALS: Token = Token(1); - poll.registry() - .register(&mut signals, SIGNALS, Interest::READABLE)?; - - let mut quit = false; - while !quit { - let wait = Duration::from_millis(100); - poll.poll(&mut events, Some(wait))?; - - for event in events.iter() { - match event.token() { - LISTENER => loop { - match listener.accept() { - Ok((connection, address)) => { - println!("Got a connection from: {:?}", address); - } - Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => break, - Err(err) => return Err(err), - } - }, - SIGNALS => while let Some(received) = signals.receive()? { - eprintln!("Received {:?} signal; exiting...", received); - quit = true; - } - _ => panic!("Unrecognized event token: {:?}", event), - } - } - } - + let ipc = ipc::Ipc::new()?; + ipc.run(); Ok(()) } diff --git a/apps/magpie/src/window.rs b/apps/magpie/src/window.rs new file mode 100644 index 0000000..4d04695 --- /dev/null +++ b/apps/magpie/src/window.rs @@ -0,0 +1,12 @@ +use canary::PanelId; +use glium::glutin; +use glutin::event::{Event, WindowEvent}; +use glutin::event_loop::{ControlFlow, EventLoop}; + +use crate::gl::Graphics; + +pub struct Window { + pub display: glium::Display, + pub graphics: Graphics, + pub panel: PanelId, +} From 40e8507d83cc0fc1e32942310d2a3e749da35bd2 Mon Sep 17 00:00:00 2001 From: mars Date: Thu, 27 Oct 2022 22:23:03 -0600 Subject: [PATCH 03/13] WindowStore + separate IPC thread --- apps/magpie/Cargo.toml | 1 + apps/magpie/src/ipc.rs | 20 +++++++++++++- apps/magpie/src/main.rs | 13 +++++++-- apps/magpie/src/window.rs | 58 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 85 insertions(+), 7 deletions(-) diff --git a/apps/magpie/Cargo.toml b/apps/magpie/Cargo.toml index 1f43cdb..a3d635e 100644 --- a/apps/magpie/Cargo.toml +++ b/apps/magpie/Cargo.toml @@ -7,5 +7,6 @@ edition = "2021" canary = { path = "../.." } glium = "0.32" mio = { version = "0.8", features = ["net", "os-poll"] } +mio-extras = "2.0" mio-signals = "0.2" slab = "0.4" diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/ipc.rs index 3cb1257..9945642 100644 --- a/apps/magpie/src/ipc.rs +++ b/apps/magpie/src/ipc.rs @@ -1,13 +1,20 @@ +use super::*; + use mio::net::UnixListener; use mio::{Events, Interest, Poll, Token}; use mio_signals::{Signal, Signals}; use slab::Slab; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; +use std::sync::mpsc::Sender; use std::time::Duration; +use crate::window::{WindowMessage, WindowMessageSender}; + const SOCK_NAME: &str = "magpie.sock"; +pub enum IpcMessage {} + /// Wraps [mio::net::UnixListener] with automatic file deletion on drop. pub struct Listener { pub uds: UnixListener, @@ -40,6 +47,8 @@ impl DerefMut for Listener { pub struct Client {} pub struct Ipc { + pub message_recv: MioReceiver, + pub window_sender: WindowMessageSender, pub poll: Poll, pub events: Events, pub quit: bool, @@ -47,11 +56,15 @@ pub struct Ipc { pub signals: Signals, pub listener_token: Token, pub signals_token: Token, + pub message_recv_token: Token, pub clients: Slab, } impl Ipc { - pub fn new() -> std::io::Result { + pub fn new( + message_recv: MioReceiver, + window_sender: WindowMessageSender, + ) -> 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(SOCK_NAME); @@ -68,6 +81,7 @@ impl Ipc { let poll = Poll::new()?; let listener_token = Token(usize::MAX); let signals_token = Token(listener_token.0 - 1); + let message_recv_token = Token(signals_token.0 - 1); let registry = poll.registry(); let interest = Interest::READABLE; @@ -75,6 +89,8 @@ impl Ipc { registry.register(&mut signals, signals_token, interest)?; Ok(Self { + message_recv, + window_sender, poll, events, quit: false, @@ -82,6 +98,7 @@ impl Ipc { signals, listener_token, signals_token, + message_recv_token, clients: Default::default(), }) } @@ -103,6 +120,7 @@ impl Ipc { } else if event.token() == self.signals_token { while let Some(received) = self.signals.receive()? { eprintln!("Received {:?} signal; exiting...", received); + let _ = self.window_sender.send_event(WindowMessage::Quit); self.quit = true; } } else { diff --git a/apps/magpie/src/main.rs b/apps/magpie/src/main.rs index c5a09e0..aa52c64 100644 --- a/apps/magpie/src/main.rs +++ b/apps/magpie/src/main.rs @@ -1,9 +1,16 @@ +use glium::glutin::event_loop::EventLoopBuilder; +use mio_extras::channel::{channel as mio_channel, Receiver as MioReceiver, Sender as MioSender}; + pub mod gl; pub mod ipc; pub mod window; fn main() -> std::io::Result<()> { - let ipc = ipc::Ipc::new()?; - ipc.run(); - Ok(()) + let (ipc_sender, ipc_recv) = mio_channel(); + let event_loop = EventLoopBuilder::::with_user_event().build(); + let window_sender = event_loop.create_proxy(); + let ipc = ipc::Ipc::new(ipc_recv, window_sender)?; + let _ipc_thread = std::thread::spawn(|| ipc.run()); + let window_store = window::WindowStore::new(ipc_sender); + window_store.run(event_loop); } diff --git a/apps/magpie/src/window.rs b/apps/magpie/src/window.rs index 4d04695..5aee058 100644 --- a/apps/magpie/src/window.rs +++ b/apps/magpie/src/window.rs @@ -1,12 +1,64 @@ -use canary::PanelId; +use super::*; + +use std::collections::HashMap; + +use glium::backend::glutin::DisplayCreationError; use glium::glutin; use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; +use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy}; +use glutin::window::WindowId; use crate::gl::Graphics; +use crate::ipc::IpcMessage; + +pub enum WindowMessage { + Quit, +} + +pub type WindowMessageSender = EventLoopProxy; pub struct Window { pub display: glium::Display, pub graphics: Graphics, - pub panel: PanelId, +} + +impl Window { + pub fn new(event_loop: &EventLoop<()>) -> Result { + let wb = glutin::window::WindowBuilder::new(); + let cb = glutin::ContextBuilder::new(); + let display = glium::Display::new(wb, cb, &event_loop)?; + let graphics = Graphics::new(&display); + + Ok(Self { display, graphics }) + } +} + +pub struct WindowStore { + pub ipc_sender: MioSender, + pub windows: HashMap, +} + +impl WindowStore { + pub fn new(ipc_sender: MioSender) -> Self { + Self { + ipc_sender, + windows: Default::default(), + } + } + + pub fn run(self, event_loop: EventLoop) -> ! { + event_loop.run(move |event, event_loop, control_flow| { + *control_flow = ControlFlow::Poll; + match event { + Event::WindowEvent { window_id, event } => match event { + _ => {} + }, + Event::MainEventsCleared => {} + Event::UserEvent(event) => match event { + WindowMessage::Quit => *control_flow = ControlFlow::Exit, + } + _ => {} + } + }); + } } From 786413ed0c5c1255fc816ee85a0cc983fefad7ea Mon Sep 17 00:00:00 2001 From: mars Date: Thu, 27 Oct 2022 22:31:02 -0600 Subject: [PATCH 04/13] Window event loop waits instead of polls --- apps/magpie/src/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/magpie/src/window.rs b/apps/magpie/src/window.rs index 5aee058..39a1da1 100644 --- a/apps/magpie/src/window.rs +++ b/apps/magpie/src/window.rs @@ -48,7 +48,7 @@ impl WindowStore { pub fn run(self, event_loop: EventLoop) -> ! { event_loop.run(move |event, event_loop, control_flow| { - *control_flow = ControlFlow::Poll; + *control_flow = ControlFlow::Wait; match event { Event::WindowEvent { window_id, event } => match event { _ => {} From a67ee571b21f3e8e002d4dc525e667e5fa50c7ba Mon Sep 17 00:00:00 2001 From: mars Date: Thu, 27 Oct 2022 22:52:42 -0600 Subject: [PATCH 05/13] IPC message sending --- apps/magpie/Cargo.toml | 1 - apps/magpie/src/ipc.rs | 39 +++++++++++++++++++++++++-------------- apps/magpie/src/main.rs | 4 +--- apps/magpie/src/window.rs | 10 ++++------ 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/apps/magpie/Cargo.toml b/apps/magpie/Cargo.toml index a3d635e..1f43cdb 100644 --- a/apps/magpie/Cargo.toml +++ b/apps/magpie/Cargo.toml @@ -7,6 +7,5 @@ edition = "2021" canary = { path = "../.." } glium = "0.32" mio = { version = "0.8", features = ["net", "os-poll"] } -mio-extras = "2.0" mio-signals = "0.2" slab = "0.4" diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/ipc.rs index 9945642..de0ee8c 100644 --- a/apps/magpie/src/ipc.rs +++ b/apps/magpie/src/ipc.rs @@ -1,20 +1,24 @@ -use super::*; - -use mio::net::UnixListener; -use mio::{Events, Interest, Poll, Token}; -use mio_signals::{Signal, Signals}; -use slab::Slab; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; -use std::sync::mpsc::Sender; +use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; +use mio::net::UnixListener; +use mio::{Events, Interest, Poll, Token, Waker}; +use mio_signals::{Signal, Signals}; +use slab::Slab; + use crate::window::{WindowMessage, WindowMessageSender}; const SOCK_NAME: &str = "magpie.sock"; pub enum IpcMessage {} +pub struct IpcMessageSender { + waker: Waker, + sender: Sender, +} + /// Wraps [mio::net::UnixListener] with automatic file deletion on drop. pub struct Listener { pub uds: UnixListener, @@ -47,7 +51,7 @@ impl DerefMut for Listener { pub struct Client {} pub struct Ipc { - pub message_recv: MioReceiver, + pub message_recv: Receiver, pub window_sender: WindowMessageSender, pub poll: Poll, pub events: Events, @@ -61,10 +65,7 @@ pub struct Ipc { } impl Ipc { - pub fn new( - message_recv: MioReceiver, - window_sender: WindowMessageSender, - ) -> std::io::Result { + pub fn new(window_sender: WindowMessageSender) -> std::io::Result<(Self, IpcMessageSender)> { 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(SOCK_NAME); @@ -88,7 +89,14 @@ impl Ipc { registry.register(&mut listener.uds, listener_token, interest)?; registry.register(&mut signals, signals_token, interest)?; - Ok(Self { + let (sender, message_recv) = channel(); + + let sender = IpcMessageSender { + waker: Waker::new(registry, message_recv_token)?, + sender, + }; + + let ipc = Self { message_recv, window_sender, poll, @@ -100,7 +108,9 @@ impl Ipc { signals_token, message_recv_token, clients: Default::default(), - }) + }; + + Ok((ipc, sender)) } pub fn poll(&mut self, timeout: Option) -> std::io::Result<()> { @@ -123,6 +133,7 @@ impl Ipc { let _ = self.window_sender.send_event(WindowMessage::Quit); self.quit = true; } + } else if let Some(client) = self.clients.get_mut(event.token().0) { } else { panic!("Unrecognized event token: {:?}", event); } diff --git a/apps/magpie/src/main.rs b/apps/magpie/src/main.rs index aa52c64..2534b24 100644 --- a/apps/magpie/src/main.rs +++ b/apps/magpie/src/main.rs @@ -1,15 +1,13 @@ use glium::glutin::event_loop::EventLoopBuilder; -use mio_extras::channel::{channel as mio_channel, Receiver as MioReceiver, Sender as MioSender}; pub mod gl; pub mod ipc; pub mod window; fn main() -> std::io::Result<()> { - let (ipc_sender, ipc_recv) = mio_channel(); let event_loop = EventLoopBuilder::::with_user_event().build(); let window_sender = event_loop.create_proxy(); - let ipc = ipc::Ipc::new(ipc_recv, window_sender)?; + let (ipc, ipc_sender) = ipc::Ipc::new(window_sender)?; let _ipc_thread = std::thread::spawn(|| ipc.run()); let window_store = window::WindowStore::new(ipc_sender); window_store.run(event_loop); diff --git a/apps/magpie/src/window.rs b/apps/magpie/src/window.rs index 39a1da1..ba48685 100644 --- a/apps/magpie/src/window.rs +++ b/apps/magpie/src/window.rs @@ -1,5 +1,3 @@ -use super::*; - use std::collections::HashMap; use glium::backend::glutin::DisplayCreationError; @@ -9,7 +7,7 @@ use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy}; use glutin::window::WindowId; use crate::gl::Graphics; -use crate::ipc::IpcMessage; +use crate::ipc::{IpcMessage, IpcMessageSender}; pub enum WindowMessage { Quit, @@ -34,12 +32,12 @@ impl Window { } pub struct WindowStore { - pub ipc_sender: MioSender, + pub ipc_sender: IpcMessageSender, pub windows: HashMap, } impl WindowStore { - pub fn new(ipc_sender: MioSender) -> Self { + pub fn new(ipc_sender: IpcMessageSender) -> Self { Self { ipc_sender, windows: Default::default(), @@ -56,7 +54,7 @@ impl WindowStore { Event::MainEventsCleared => {} Event::UserEvent(event) => match event { WindowMessage::Quit => *control_flow = ControlFlow::Exit, - } + }, _ => {} } }); From e1230c97101103ee8577e65d4ace22bfffb9a428 Mon Sep 17 00:00:00 2001 From: mars Date: Fri, 28 Oct 2022 23:22:36 -0600 Subject: [PATCH 06/13] IPC client connections --- apps/magpie/Cargo.toml | 1 + apps/magpie/src/ipc.rs | 69 +++++++++++++++++++++++++++++++++++++++-- apps/magpie/src/main.rs | 2 +- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/apps/magpie/Cargo.toml b/apps/magpie/Cargo.toml index 1f43cdb..1d0b586 100644 --- a/apps/magpie/Cargo.toml +++ b/apps/magpie/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] canary = { path = "../.." } glium = "0.32" +magpie-types = { path = "../../crates/magpie-types" } mio = { version = "0.8", features = ["net", "os-poll"] } mio-signals = "0.2" slab = "0.4" diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/ipc.rs index de0ee8c..b41059c 100644 --- a/apps/magpie/src/ipc.rs +++ b/apps/magpie/src/ipc.rs @@ -1,9 +1,11 @@ +use std::io::Read; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; +use std::str::from_utf8; use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; -use mio::net::UnixListener; +use mio::net::{UnixListener, UnixStream}; use mio::{Events, Interest, Poll, Token, Waker}; use mio_signals::{Signal, Signals}; use slab::Slab; @@ -48,7 +50,50 @@ impl DerefMut for Listener { } } -pub struct Client {} +pub struct Client { + connection: UnixStream, +} + +impl Client { + pub fn new(connection: UnixStream) -> Self { + Self { connection } + } + + pub fn on_readable(&mut self) -> std::io::Result { + let mut connection_closed = false; + let mut received_data = vec![0; 4096]; + let mut bytes_read = 0; + + loop { + match self.connection.read(&mut received_data[bytes_read..]) { + Ok(0) => { + connection_closed = true; + break; + } + Ok(n) => { + bytes_read += n; + if bytes_read >= received_data.len() { + received_data.resize(received_data.len() + 1024, 0); + } + } + 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), + } + } + + if bytes_read > 0 { + let received_data = &received_data[..bytes_read]; + if let Ok(str_buf) = from_utf8(received_data) { + println!("Received data: {}", str_buf.trim_end()); + } else { + println!("Received (non-UTF-8) data: {:?}", received_data); + } + } + + Ok(connection_closed) + } +} pub struct Ipc { pub message_recv: Receiver, @@ -121,7 +166,19 @@ impl Ipc { loop { match self.listener.accept() { Ok((connection, address)) => { - println!("Got a connection from: {:?}", address); + let token = Token(self.clients.vacant_key()); + println!( + "Accepting connection (Client #{}) from {:?}", + token.0, address + ); + let mut client = Client::new(connection); + let interest = Interest::READABLE; + self.poll.registry().register( + &mut client.connection, + token, + interest, + )?; + self.clients.insert(client); } Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => break, Err(err) => return Err(err), @@ -134,6 +191,12 @@ impl Ipc { self.quit = true; } } else if let Some(client) = self.clients.get_mut(event.token().0) { + let disconnected = client.on_readable()?; + if disconnected { + println!("Client #{} disconnected", event.token().0); + let mut client = self.clients.remove(event.token().0); + self.poll.registry().deregister(&mut client.connection)?; + } } else { panic!("Unrecognized event token: {:?}", event); } diff --git a/apps/magpie/src/main.rs b/apps/magpie/src/main.rs index 2534b24..333109f 100644 --- a/apps/magpie/src/main.rs +++ b/apps/magpie/src/main.rs @@ -10,5 +10,5 @@ fn main() -> std::io::Result<()> { let (ipc, ipc_sender) = ipc::Ipc::new(window_sender)?; let _ipc_thread = std::thread::spawn(|| ipc.run()); let window_store = window::WindowStore::new(ipc_sender); - window_store.run(event_loop); + window_store.run(event_loop) } From 4e46832cd0ae8c6680f188bc2f8c644b5eccea41 Mon Sep 17 00:00:00 2001 From: mars Date: Fri, 28 Oct 2022 23:22:51 -0600 Subject: [PATCH 07/13] magpie-client and magpie-types --- Cargo.toml | 1 + crates/magpie-client/Cargo.toml | 8 +++++++ crates/magpie-client/src/lib.rs | 29 ++++++++++++++++++++++++++ crates/magpie-types/src/lib.rs | 37 ++++++++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 crates/magpie-client/Cargo.toml create mode 100644 crates/magpie-client/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index c1a0013..e821b37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "apps/magpie", "apps/music-player", "apps/sandbox", + "crates/magpie-client", "crates/magpie-types", "crates/script", "crates/types", diff --git a/crates/magpie-client/Cargo.toml b/crates/magpie-client/Cargo.toml new file mode 100644 index 0000000..0c66102 --- /dev/null +++ b/crates/magpie-client/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "magpie-client" +version = "0.1.0" +edition = "2021" + +[dependencies] +magpie-types = { path = "../magpie-types" } +serde_json = "1" diff --git a/crates/magpie-client/src/lib.rs b/crates/magpie-client/src/lib.rs new file mode 100644 index 0000000..d4c13b5 --- /dev/null +++ b/crates/magpie-client/src/lib.rs @@ -0,0 +1,29 @@ +use std::io::Write; +use std::os::unix::net::UnixStream; +use std::path::Path; + +use magpie_types::MagpieServerMsg; + +/// The name of the Magpie server socket. +pub const MAGPIE_SOCK: &str = "magpie.sock"; + +/// A client to a Magpie server. +pub struct MagpieClient { + socket: UnixStream, +} + +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 { socket }) + } + + pub fn send_msg(&mut self, msg: &MagpieServerMsg) -> std::io::Result<()> { + let bytes = serde_json::to_vec(msg).unwrap(); + self.socket.write_all(&bytes)?; + Ok(()) + } +} diff --git a/crates/magpie-types/src/lib.rs b/crates/magpie-types/src/lib.rs index 5befe6a..80c070d 100644 --- a/crates/magpie-types/src/lib.rs +++ b/crates/magpie-types/src/lib.rs @@ -1 +1,36 @@ -// protocol types go here +use serde::{Deserialize, Serialize}; + +/// 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, +} + +/// 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 {} From 63691ebc2875f352abb347137e670a90bc91a22f Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 29 Oct 2022 14:42:41 -0600 Subject: [PATCH 08/13] Add Messenger struct --- apps/magpie/src/ipc.rs | 60 +++++++------------ crates/magpie-client/Cargo.toml | 1 - crates/magpie-client/src/lib.rs | 17 +++--- crates/magpie-types/Cargo.toml | 2 + crates/magpie-types/src/lib.rs | 102 +++++++++++++++++++++++++++++++- 5 files changed, 130 insertions(+), 52 deletions(-) diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/ipc.rs index b41059c..7feaca1 100644 --- a/apps/magpie/src/ipc.rs +++ b/apps/magpie/src/ipc.rs @@ -5,6 +5,7 @@ use std::str::from_utf8; use std::sync::mpsc::{channel, Receiver, Sender}; use std::time::Duration; +use magpie_types::ServerMessenger; use mio::net::{UnixListener, UnixStream}; use mio::{Events, Interest, Poll, Token, Waker}; use mio_signals::{Signal, Signals}; @@ -51,47 +52,26 @@ impl DerefMut for Listener { } pub struct Client { - connection: UnixStream, + messenger: ServerMessenger, + token: Token, } impl Client { - pub fn new(connection: UnixStream) -> Self { - Self { connection } + pub fn new(connection: UnixStream, token: Token) -> Self { + Self { + messenger: ServerMessenger::new(connection), + token, + } } pub fn on_readable(&mut self) -> std::io::Result { - let mut connection_closed = false; - let mut received_data = vec![0; 4096]; - let mut bytes_read = 0; + self.messenger.flush_recv()?; - loop { - match self.connection.read(&mut received_data[bytes_read..]) { - Ok(0) => { - connection_closed = true; - break; - } - Ok(n) => { - bytes_read += n; - if bytes_read >= received_data.len() { - received_data.resize(received_data.len() + 1024, 0); - } - } - 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), - } + while let Some(msg) = self.messenger.recv() { + println!("Client #{}: {:?}", self.token.0, msg); } - if bytes_read > 0 { - let received_data = &received_data[..bytes_read]; - if let Ok(str_buf) = from_utf8(received_data) { - println!("Received data: {}", str_buf.trim_end()); - } else { - println!("Received (non-UTF-8) data: {:?}", received_data); - } - } - - Ok(connection_closed) + Ok(self.messenger.is_closed()) } } @@ -165,19 +145,19 @@ impl Ipc { if event.token() == self.listener_token { loop { match self.listener.accept() { - Ok((connection, address)) => { + Ok((mut connection, address)) => { let token = Token(self.clients.vacant_key()); println!( "Accepting connection (Client #{}) from {:?}", token.0, address ); - let mut client = Client::new(connection); + let interest = Interest::READABLE; - self.poll.registry().register( - &mut client.connection, - token, - interest, - )?; + self.poll + .registry() + .register(&mut connection, token, interest)?; + + let client = Client::new(connection, token); self.clients.insert(client); } Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => break, @@ -195,7 +175,7 @@ impl Ipc { if disconnected { println!("Client #{} disconnected", event.token().0); let mut client = self.clients.remove(event.token().0); - self.poll.registry().deregister(&mut client.connection)?; + self.poll.registry().deregister(&mut client.messenger.transport)?; } } else { panic!("Unrecognized event token: {:?}", event); diff --git a/crates/magpie-client/Cargo.toml b/crates/magpie-client/Cargo.toml index 0c66102..98ec6fc 100644 --- a/crates/magpie-client/Cargo.toml +++ b/crates/magpie-client/Cargo.toml @@ -5,4 +5,3 @@ edition = "2021" [dependencies] magpie-types = { path = "../magpie-types" } -serde_json = "1" diff --git a/crates/magpie-client/src/lib.rs b/crates/magpie-client/src/lib.rs index d4c13b5..9a19133 100644 --- a/crates/magpie-client/src/lib.rs +++ b/crates/magpie-client/src/lib.rs @@ -1,15 +1,16 @@ -use std::io::Write; +pub use magpie_types; + use std::os::unix::net::UnixStream; use std::path::Path; -use magpie_types::MagpieServerMsg; +use magpie_types::ClientMessenger; /// The name of the Magpie server socket. pub const MAGPIE_SOCK: &str = "magpie.sock"; /// A client to a Magpie server. pub struct MagpieClient { - socket: UnixStream, + pub messenger: ClientMessenger, } impl MagpieClient { @@ -18,12 +19,8 @@ impl MagpieClient { let sock_dir = Path::new(&sock_dir); let sock_path = sock_dir.join(MAGPIE_SOCK); let socket = UnixStream::connect(sock_path)?; - Ok(Self { socket }) - } - - pub fn send_msg(&mut self, msg: &MagpieServerMsg) -> std::io::Result<()> { - let bytes = serde_json::to_vec(msg).unwrap(); - self.socket.write_all(&bytes)?; - Ok(()) + Ok(Self { + messenger: ClientMessenger::new(socket), + }) } } diff --git a/crates/magpie-types/Cargo.toml b/crates/magpie-types/Cargo.toml index 12f347a..2a2b256 100644 --- a/crates/magpie-types/Cargo.toml +++ b/crates/magpie-types/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] +byteorder = "1.4" serde = { version = "1.0", features = ["derive"] } +serde_json = "1" diff --git a/crates/magpie-types/src/lib.rs b/crates/magpie-types/src/lib.rs index 80c070d..e99be2a 100644 --- a/crates/magpie-types/src/lib.rs +++ b/crates/magpie-types/src/lib.rs @@ -1,4 +1,8 @@ -use serde::{Deserialize, Serialize}; +use std::collections::VecDeque; +use std::io::{Read, Write}; +use std::marker::PhantomData; + +use serde::{de::DeserializeOwned, Deserialize, Serialize}; /// An identifier for a Magpie panel. /// @@ -34,3 +38,99 @@ pub enum MagpieServerMsg { #[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)?; + 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 expected_len < self.received_buf.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() + } +} From e624edc36f1678f3f31cbab8177e76e0ba3f7398 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 29 Oct 2022 16:54:51 -0600 Subject: [PATCH 09/13] Open/close window events --- apps/magpie/Cargo.toml | 1 + apps/magpie/src/ipc.rs | 59 +++++++++++++++++++++++++------------- apps/magpie/src/window.rs | 60 +++++++++++++++++++++++++++++---------- 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/apps/magpie/Cargo.toml b/apps/magpie/Cargo.toml index 1d0b586..da94c15 100644 --- a/apps/magpie/Cargo.toml +++ b/apps/magpie/Cargo.toml @@ -9,4 +9,5 @@ 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" diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/ipc.rs index 7feaca1..51dbdd1 100644 --- a/apps/magpie/src/ipc.rs +++ b/apps/magpie/src/ipc.rs @@ -3,12 +3,14 @@ use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; use std::str::from_utf8; use std::sync::mpsc::{channel, Receiver, Sender}; +use std::sync::Arc; use std::time::Duration; -use magpie_types::ServerMessenger; +use magpie_types::{CreatePanel, MagpieClientMsg, MagpieServerMsg, 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}; @@ -51,24 +53,31 @@ impl DerefMut for Listener { } } +pub struct IpcData { + poll: Poll, +} + pub struct Client { + data: Arc>, + window_sender: WindowMessageSender, messenger: ServerMessenger, token: Token, } impl Client { - pub fn new(connection: UnixStream, token: Token) -> Self { - Self { - messenger: ServerMessenger::new(connection), - token, - } - } - pub fn on_readable(&mut self) -> std::io::Result { self.messenger.flush_recv()?; while let Some(msg) = self.messenger.recv() { println!("Client #{}: {:?}", self.token.0, msg); + match msg { + MagpieServerMsg::CreatePanel(CreatePanel { id }) => { + let id = id as usize; + let msg = WindowMessage::OpenWindow { id }; + let _ = self.window_sender.send_event(msg); + } + _ => unimplemented!(), + } } Ok(self.messenger.is_closed()) @@ -76,9 +85,9 @@ impl Client { } pub struct Ipc { - pub message_recv: Receiver, + pub data: Arc>, pub window_sender: WindowMessageSender, - pub poll: Poll, + pub message_recv: Receiver, pub events: Events, pub quit: bool, pub listener: Listener, @@ -121,10 +130,12 @@ impl Ipc { sender, }; + let data = IpcData { poll }; + let ipc = Self { - message_recv, + data: Arc::new(RwLock::new(data)), window_sender, - poll, + message_recv, events, quit: false, listener, @@ -139,7 +150,7 @@ impl Ipc { } pub fn poll(&mut self, timeout: Option) -> std::io::Result<()> { - self.poll.poll(&mut self.events, timeout)?; + self.data.write().poll.poll(&mut self.events, timeout)?; for event in self.events.iter() { if event.token() == self.listener_token { @@ -153,12 +164,18 @@ impl Ipc { ); let interest = Interest::READABLE; - self.poll - .registry() - .register(&mut connection, token, interest)?; + self.data.write().poll.registry().register( + &mut connection, + token, + interest, + )?; - let client = Client::new(connection, token); - self.clients.insert(client); + self.clients.insert(Client { + messenger: ServerMessenger::new(connection), + token, + data: self.data.clone(), + window_sender: self.window_sender.clone(), + }); } Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => break, Err(err) => return Err(err), @@ -175,7 +192,11 @@ impl Ipc { if disconnected { println!("Client #{} disconnected", event.token().0); let mut client = self.clients.remove(event.token().0); - self.poll.registry().deregister(&mut client.messenger.transport)?; + self.data + .write() + .poll + .registry() + .deregister(&mut client.messenger.transport)?; } } else { panic!("Unrecognized event token: {:?}", event); diff --git a/apps/magpie/src/window.rs b/apps/magpie/src/window.rs index ba48685..c6f5199 100644 --- a/apps/magpie/src/window.rs +++ b/apps/magpie/src/window.rs @@ -1,15 +1,17 @@ use std::collections::HashMap; use glium::backend::glutin::DisplayCreationError; -use glium::glutin; +use glium::{glutin, Surface}; use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy}; +use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget}; use glutin::window::WindowId; use crate::gl::Graphics; use crate::ipc::{IpcMessage, IpcMessageSender}; pub enum WindowMessage { + OpenWindow { id: usize }, + CloseWindow { id: usize }, Quit, } @@ -21,7 +23,9 @@ pub struct Window { } impl Window { - pub fn new(event_loop: &EventLoop<()>) -> Result { + pub fn new( + event_loop: &EventLoopWindowTarget, + ) -> Result { let wb = glutin::window::WindowBuilder::new(); let cb = glutin::ContextBuilder::new(); let display = glium::Display::new(wb, cb, &event_loop)?; @@ -29,10 +33,15 @@ impl Window { Ok(Self { display, graphics }) } + + pub fn request_redraw(&mut self) { + self.display.gl_window().window().request_redraw(); + } } pub struct WindowStore { pub ipc_sender: IpcMessageSender, + pub ipc_to_window: HashMap, pub windows: HashMap, } @@ -40,23 +49,44 @@ impl WindowStore { pub fn new(ipc_sender: IpcMessageSender) -> Self { Self { ipc_sender, + ipc_to_window: Default::default(), windows: Default::default(), } } - pub fn run(self, event_loop: EventLoop) -> ! { - event_loop.run(move |event, event_loop, control_flow| { - *control_flow = ControlFlow::Wait; - match event { - Event::WindowEvent { window_id, event } => match event { - _ => {} - }, - Event::MainEventsCleared => {} - Event::UserEvent(event) => match event { - WindowMessage::Quit => *control_flow = ControlFlow::Exit, - }, - _ => {} + pub fn run(mut self, event_loop: EventLoop) -> ! { + event_loop.run(move |event, event_loop, control_flow| match event { + Event::WindowEvent { window_id, event } => { + if let Some(window) = self.windows.get_mut(&window_id) { + match event { + WindowEvent::Resized(_) => window.request_redraw(), + _ => {} + } + } } + Event::RedrawRequested(id) => { + if let Some(window) = self.windows.get(&id) { + let mut target = window.display.draw(); + target.clear_color(0.0, 0.0, 0.0, 1.0); + target.finish().unwrap(); + } + } + Event::UserEvent(event) => match event { + WindowMessage::OpenWindow { id } => { + println!("Opening window {}", id); + let window = Window::new(&event_loop).unwrap(); + let window_id = window.display.gl_window().window().id(); + self.windows.insert(window_id, window); + self.ipc_to_window.insert(id, window_id); + } + WindowMessage::CloseWindow { id } => { + if let Some(window_id) = self.ipc_to_window.remove(&id) { + self.windows.remove(&window_id); + } + } + WindowMessage::Quit => *control_flow = ControlFlow::Exit, + }, + _ => {} }); } } From 28c6a3e1e60a18efce1e769ace49e01cc44f0970 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 29 Oct 2022 17:07:34 -0600 Subject: [PATCH 10/13] Automatically close client's windows on disconnect --- apps/magpie/src/ipc.rs | 55 ++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/ipc.rs index 51dbdd1..f464780 100644 --- a/apps/magpie/src/ipc.rs +++ b/apps/magpie/src/ipc.rs @@ -1,12 +1,11 @@ -use std::io::Read; +use std::collections::HashMap; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; -use std::str::from_utf8; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; use std::time::Duration; -use magpie_types::{CreatePanel, MagpieClientMsg, MagpieServerMsg, ServerMessenger}; +use magpie_types::{CreatePanel, MagpieServerMsg, ServerMessenger}; use mio::net::{UnixListener, UnixStream}; use mio::{Events, Interest, Poll, Token, Waker}; use mio_signals::{Signal, Signals}; @@ -55,6 +54,15 @@ impl DerefMut for Listener { pub struct IpcData { poll: Poll, + next_window_id: usize, +} + +impl IpcData { + pub fn new_window_id(&mut self) -> usize { + let id = self.next_window_id; + self.next_window_id = self.next_window_id.wrapping_add(1); + id + } } pub struct Client { @@ -62,6 +70,23 @@ pub struct Client { window_sender: WindowMessageSender, messenger: ServerMessenger, token: Token, + id_to_window: HashMap, +} + +impl Drop for Client { + fn drop(&mut self) { + println!("Client #{} disconnected", self.token.0); + let data = self.data.write(); + let _ = data + .poll + .registry() + .deregister(&mut self.messenger.transport); + + for (_id, window) in self.id_to_window.drain() { + let msg = WindowMessage::CloseWindow { id: window }; + let _ = self.window_sender.send_event(msg); + } + } } impl Client { @@ -72,8 +97,14 @@ impl Client { println!("Client #{}: {:?}", self.token.0, msg); match msg { MagpieServerMsg::CreatePanel(CreatePanel { id }) => { - let id = id as usize; - let msg = WindowMessage::OpenWindow { id }; + let window = self.data.write().new_window_id(); + + if let Some(old_id) = self.id_to_window.insert(id, window) { + let msg = WindowMessage::CloseWindow { id: old_id }; + let _ = self.window_sender.send_event(msg); + } + + let msg = WindowMessage::OpenWindow { id: window }; let _ = self.window_sender.send_event(msg); } _ => unimplemented!(), @@ -130,7 +161,10 @@ impl Ipc { sender, }; - let data = IpcData { poll }; + let data = IpcData { + poll, + next_window_id: 0, + }; let ipc = Self { data: Arc::new(RwLock::new(data)), @@ -175,6 +209,7 @@ impl Ipc { token, data: self.data.clone(), window_sender: self.window_sender.clone(), + id_to_window: Default::default(), }); } Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => break, @@ -190,13 +225,7 @@ impl Ipc { } else if let Some(client) = self.clients.get_mut(event.token().0) { let disconnected = client.on_readable()?; if disconnected { - println!("Client #{} disconnected", event.token().0); - let mut client = self.clients.remove(event.token().0); - self.data - .write() - .poll - .registry() - .deregister(&mut client.messenger.transport)?; + self.clients.remove(event.token().0); } } else { panic!("Unrecognized event token: {:?}", event); From a058d027effcec5d1bc5bc8ae8e78cd8a9082857 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 29 Oct 2022 17:23:56 -0600 Subject: [PATCH 11/13] Draw Canary DrawCommands --- apps/magpie/src/gl.rs | 50 ++++++++++++++++++++++++++++++++++++--- apps/magpie/src/window.rs | 19 ++++++++------- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/apps/magpie/src/gl.rs b/apps/magpie/src/gl.rs index c1ea4cb..bcecb37 100644 --- a/apps/magpie/src/gl.rs +++ b/apps/magpie/src/gl.rs @@ -1,3 +1,4 @@ +use canary::DrawCommand; use glium::{glutin, Display, Surface}; #[derive(Copy, Clone)] @@ -45,14 +46,57 @@ const FRAGMENT_SHADER_SRC: &str = r#" "#; pub struct Graphics { + pub display: glium::Display, pub program: glium::Program, } impl Graphics { - pub fn new(display: &glium::Display) -> Self { + pub fn new(display: glium::Display) -> Self { let program = - glium::Program::from_source(display, VERTEX_SHADER_SRC, FRAGMENT_SHADER_SRC, None) + glium::Program::from_source(&display, VERTEX_SHADER_SRC, FRAGMENT_SHADER_SRC, None) .unwrap(); - Self { program } + Self { display, program } + } + + pub fn draw(&mut self, commands: &[DrawCommand]) { + let mut joined_vs: Vec = Vec::new(); + let mut joined_is = Vec::new(); + + for command in commands.iter() { + match command { + canary::DrawCommand::Mesh { vertices, indices } => { + let voff = joined_vs.len() as canary::MeshIndex; + joined_vs.extend(vertices.iter().map(Vertex::from)); + joined_is.extend(indices.iter().map(|i| i + voff)); + } + _ => unimplemented!(), + } + } + + let vertex_buffer = glium::VertexBuffer::new(&self.display, &joined_vs).unwrap(); + let index_buffer = glium::IndexBuffer::new( + &self.display, + glium::index::PrimitiveType::TrianglesList, + &joined_is, + ) + .unwrap(); + + let params = glium::DrawParameters { + blend: glium::Blend::alpha_blending(), + ..Default::default() + }; + + let mut target = self.display.draw(); + target.clear_color(0.0, 0.0, 0.0, 1.0); + target + .draw( + &vertex_buffer, + &index_buffer, + &self.program, + &glium::uniforms::EmptyUniforms, + ¶ms, + ) + .unwrap(); + target.finish().unwrap(); } } diff --git a/apps/magpie/src/window.rs b/apps/magpie/src/window.rs index c6f5199..97a47e0 100644 --- a/apps/magpie/src/window.rs +++ b/apps/magpie/src/window.rs @@ -18,7 +18,6 @@ pub enum WindowMessage { pub type WindowMessageSender = EventLoopProxy; pub struct Window { - pub display: glium::Display, pub graphics: Graphics, } @@ -29,13 +28,17 @@ impl Window { let wb = glutin::window::WindowBuilder::new(); let cb = glutin::ContextBuilder::new(); let display = glium::Display::new(wb, cb, &event_loop)?; - let graphics = Graphics::new(&display); + let graphics = Graphics::new(display); - Ok(Self { display, graphics }) + Ok(Self { graphics }) + } + + pub fn get_id(&self) -> WindowId { + self.graphics.display.gl_window().window().id() } pub fn request_redraw(&mut self) { - self.display.gl_window().window().request_redraw(); + self.graphics.display.gl_window().window().request_redraw(); } } @@ -65,17 +68,15 @@ impl WindowStore { } } Event::RedrawRequested(id) => { - if let Some(window) = self.windows.get(&id) { - let mut target = window.display.draw(); - target.clear_color(0.0, 0.0, 0.0, 1.0); - target.finish().unwrap(); + if let Some(window) = self.windows.get_mut(&id) { + window.graphics.draw(&[]); } } Event::UserEvent(event) => match event { WindowMessage::OpenWindow { id } => { println!("Opening window {}", id); let window = Window::new(&event_loop).unwrap(); - let window_id = window.display.gl_window().window().id(); + let window_id = window.get_id(); self.windows.insert(window_id, window); self.ipc_to_window.insert(id, window_id); } From 839f8c6c6e376c0ad88cc737ffe9c78d16b15c65 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 29 Oct 2022 18:00:46 -0600 Subject: [PATCH 12/13] Magpie loads, draws, and updates scripts --- apps/magpie/src/ipc.rs | 4 +-- apps/magpie/src/window.rs | 53 +++++++++++++++++++++++++++++----- crates/magpie-types/src/lib.rs | 2 ++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/apps/magpie/src/ipc.rs b/apps/magpie/src/ipc.rs index f464780..1fefd94 100644 --- a/apps/magpie/src/ipc.rs +++ b/apps/magpie/src/ipc.rs @@ -96,7 +96,7 @@ impl Client { while let Some(msg) = self.messenger.recv() { println!("Client #{}: {:?}", self.token.0, msg); match msg { - MagpieServerMsg::CreatePanel(CreatePanel { id }) => { + MagpieServerMsg::CreatePanel(CreatePanel { id, script }) => { let window = self.data.write().new_window_id(); if let Some(old_id) = self.id_to_window.insert(id, window) { @@ -104,7 +104,7 @@ impl Client { let _ = self.window_sender.send_event(msg); } - let msg = WindowMessage::OpenWindow { id: window }; + let msg = WindowMessage::OpenWindow { id: window, script }; let _ = self.window_sender.send_event(msg); } _ => unimplemented!(), diff --git a/apps/magpie/src/window.rs b/apps/magpie/src/window.rs index 97a47e0..fe8a016 100644 --- a/apps/magpie/src/window.rs +++ b/apps/magpie/src/window.rs @@ -1,5 +1,8 @@ use std::collections::HashMap; +use std::path::PathBuf; +use std::time::Instant; +use canary::{PanelId, ScriptAbiImpl, ScriptInstance, WasmtimeRuntime, WasmtimeScript}; use glium::backend::glutin::DisplayCreationError; use glium::{glutin, Surface}; use glutin::event::{Event, WindowEvent}; @@ -10,7 +13,7 @@ use crate::gl::Graphics; use crate::ipc::{IpcMessage, IpcMessageSender}; pub enum WindowMessage { - OpenWindow { id: usize }, + OpenWindow { id: usize, script: PathBuf }, CloseWindow { id: usize }, Quit, } @@ -19,20 +22,30 @@ pub type WindowMessageSender = EventLoopProxy; pub struct Window { pub graphics: Graphics, + pub script: WasmtimeScript, + pub panel: PanelId, + pub last_update: Instant, } impl Window { pub fn new( + script: WasmtimeScript, + panel: PanelId, event_loop: &EventLoopWindowTarget, ) -> Result { let wb = glutin::window::WindowBuilder::new(); let cb = glutin::ContextBuilder::new(); let display = glium::Display::new(wb, cb, &event_loop)?; let graphics = Graphics::new(display); - - Ok(Self { graphics }) + let last_update = Instant::now(); + Ok(Self { + graphics, + script, + panel, + last_update, + }) } - + pub fn get_id(&self) -> WindowId { self.graphics.display.gl_window().window().id() } @@ -40,12 +53,25 @@ impl Window { pub fn request_redraw(&mut self) { self.graphics.display.gl_window().window().request_redraw(); } + + pub fn update(&mut self) { + let now = Instant::now(); + let dt = now.duration_since(self.last_update).as_secs_f32(); + self.script.update(self.panel, dt); + } + + pub fn draw(&mut self) { + self.script.draw(self.panel, |commands| { + self.graphics.draw(commands); + }); + } } pub struct WindowStore { pub ipc_sender: IpcMessageSender, pub ipc_to_window: HashMap, pub windows: HashMap, + pub runtime: WasmtimeRuntime, } impl WindowStore { @@ -54,6 +80,7 @@ impl WindowStore { ipc_sender, ipc_to_window: Default::default(), windows: Default::default(), + runtime: WasmtimeRuntime::new().unwrap(), } } @@ -69,13 +96,23 @@ impl WindowStore { } Event::RedrawRequested(id) => { if let Some(window) = self.windows.get_mut(&id) { - window.graphics.draw(&[]); + window.draw(); + } + } + Event::MainEventsCleared => { + for (_id, window) in self.windows.iter_mut() { + window.update(); + window.request_redraw(); } } Event::UserEvent(event) => match event { - WindowMessage::OpenWindow { id } => { - println!("Opening window {}", id); - let window = Window::new(&event_loop).unwrap(); + WindowMessage::OpenWindow { id, script } => { + println!("Opening window {} with script {:?}", id, script); + let abi = Default::default(); + let module = std::fs::read(script).unwrap(); + let mut script = self.runtime.load_module(abi, &module).unwrap(); + let panel = script.bind_panel(vec![]); + let window = Window::new(script, panel, &event_loop).unwrap(); let window_id = window.get_id(); self.windows.insert(window_id, window); self.ipc_to_window.insert(id, window_id); diff --git a/crates/magpie-types/src/lib.rs b/crates/magpie-types/src/lib.rs index e99be2a..9d19471 100644 --- a/crates/magpie-types/src/lib.rs +++ b/crates/magpie-types/src/lib.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use std::io::{Read, Write}; use std::marker::PhantomData; +use std::path::PathBuf; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -17,6 +18,7 @@ pub type PanelId = u32; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CreatePanel { pub id: PanelId, + pub script: PathBuf, } /// Sends a panel a message. From 4bbf60a73748ce86776b94dd59b5fc412b2c747e Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 29 Oct 2022 23:26:02 -0600 Subject: [PATCH 13/13] Music player uses Magpie --- apps/music-player/Cargo.toml | 4 +- apps/music-player/src/main.rs | 209 ++++++---------------------------- 2 files changed, 33 insertions(+), 180 deletions(-) diff --git a/apps/music-player/Cargo.toml b/apps/music-player/Cargo.toml index 88dc264..fe9c724 100644 --- a/apps/music-player/Cargo.toml +++ b/apps/music-player/Cargo.toml @@ -4,8 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] -anyhow = "1" -canary = { path = "../../" } -glium = "0.32" +magpie-client = { path = "../../crates/magpie-client" } mpris = "2.0.0-rc3" ouroboros = "^0.15" diff --git a/apps/music-player/src/main.rs b/apps/music-player/src/main.rs index 888a3a1..c979e5c 100644 --- a/apps/music-player/src/main.rs +++ b/apps/music-player/src/main.rs @@ -1,54 +1,8 @@ -use canary::ScriptInstance; -use glium::{glutin, Display, Surface}; -use glutin::event::{Event, WindowEvent}; -use glutin::event_loop::{ControlFlow, EventLoop}; +use magpie_client::{magpie_types, MagpieClient}; +use magpie_types::{CreatePanel, MagpieServerMsg}; use mpris::{PlayerFinder, ProgressTracker}; use ouroboros::self_referencing; -#[derive(Copy, Clone)] -pub struct Vertex { - pub position: [f32; 2], - pub color: [u8; 4], -} - -glium::implement_vertex!(Vertex, position normalize(false), color normalize(true)); - -impl From<&canary::MeshVertex> for Vertex { - fn from(v: &canary::MeshVertex) -> Self { - let (r, g, b, a) = v.color.to_rgba_unmultiplied(); - Self { - position: [v.position.x, v.position.y], - color: [r, g, b, a], - } - } -} - -const VERTEX_SHADER_SRC: &str = r#" - #version 330 - - in vec2 position; - in vec4 color; - - out vec4 frag_color; - - void main() { - gl_Position = vec4(position, 0.0, 1.0); - frag_color = color; - } -"#; - -const FRAGMENT_SHADER_SRC: &str = r#" - #version 330 - - in vec4 frag_color; - - out vec4 fb_color; - - void main() { - fb_color = frag_color; - } -"#; - #[self_referencing] pub struct Player { player: mpris::Player, @@ -60,6 +14,7 @@ pub struct Player { impl Player { pub fn from_player(player: mpris::Player) -> Self { + println!("Connected to {}", player.identity()); PlayerBuilder { player, tracker_builder: |player| player.track_progress(100).unwrap(), @@ -72,10 +27,14 @@ impl Player { let tick = tracker.tick(); if tick.progress_changed { + let p = tick.progress; println!( - "{:?}/{:?}", - tick.progress.position(), - tick.progress.length() + "({:?}/{:?}) {:?} - {:?} - {:?}", + p.position(), + p.length(), + p.metadata().artists(), + p.metadata().album_name(), + p.metadata().title(), ); } @@ -88,109 +47,6 @@ impl Player { } } -pub struct App { - display: Display, - program: glium::Program, - player_finder: PlayerFinder, - player: Option, - script: canary::WasmtimeScript, - panel: canary::PanelId, -} - -impl App { - pub fn new(module_path: &str, display: Display) -> Self { - let program = - glium::Program::from_source(&display, VERTEX_SHADER_SRC, FRAGMENT_SHADER_SRC, None) - .unwrap(); - - let runtime = canary::WasmtimeRuntime::new().unwrap(); - let abi = Default::default(); - let module = std::fs::read(module_path).unwrap(); - let mut script = runtime.load_module(abi, &module).unwrap(); - let panel = script.bind_panel(vec![]); - - Self { - display, - program, - player_finder: PlayerFinder::new().expect("Could not connect to D-Bus"), - player: None, - script, - panel, - } - } - - pub fn update(&mut self) { - self.display.gl_window().window().request_redraw(); - - /*let disconnected = if let Some(player) = self.player.as_mut() { - player.tick() - } else { - match self.player_finder.find_active() { - Ok(player) => { - println!("Connected to MPRIS"); - self.player = Some(Player::from_player(player)); - } - Err(e) => eprintln!("PlayerFinder: {:?}", e), - } - - false - }; - - if disconnected { - println!("Disconnected from MPRIS"); - self.player = None; - }*/ - - self.script.update(self.panel, 0.01667); - } - - pub fn draw(&mut self) { - let mut target = self.display.draw(); - target.clear_color(0.0, 0.0, 0.0, 0.0); - - self.script.draw(self.panel, |commands| { - let mut joined_vs: Vec = Vec::new(); - let mut joined_is = Vec::new(); - - for command in commands.iter() { - match command { - canary::DrawCommand::Mesh { vertices, indices } => { - let voff = joined_vs.len() as canary::MeshIndex; - joined_vs.extend(vertices.iter().map(Vertex::from)); - joined_is.extend(indices.iter().map(|i| i + voff)); - } - _ => unimplemented!(), - } - } - - let vertex_buffer = glium::VertexBuffer::new(&self.display, &joined_vs).unwrap(); - let index_buffer = glium::IndexBuffer::new( - &self.display, - glium::index::PrimitiveType::TrianglesList, - &joined_is, - ) - .unwrap(); - - let params = glium::DrawParameters { - blend: glium::Blend::alpha_blending(), - ..Default::default() - }; - - target - .draw( - &vertex_buffer, - &index_buffer, - &self.program, - &glium::uniforms::EmptyUniforms, - ¶ms, - ) - .unwrap(); - }); - - target.finish().unwrap(); - } -} - fn main() { let args: Vec = std::env::args().collect(); let module_path = args @@ -198,30 +54,29 @@ fn main() { .expect("Please pass a path to a Canary script!") .to_owned(); - let event_loop = EventLoop::new(); - let wb = glutin::window::WindowBuilder::new(); - let cb = glutin::ContextBuilder::new(); - let display = glium::Display::new(wb, cb, &event_loop).unwrap(); + let player_finder = PlayerFinder::new().expect("Could not connect to D-Bus"); - let mut app = App::new(&module_path, display); + let mut magpie = MagpieClient::new().unwrap(); + let script = std::path::PathBuf::from(&module_path); + let msg = CreatePanel { id: 0, script }; + let msg = MagpieServerMsg::CreatePanel(msg); + magpie.messenger.send(&msg).unwrap(); - event_loop.run(move |ev, _, control_flow| { - *control_flow = ControlFlow::Poll; - match ev { - Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - return; - } - _ => return, - }, - Event::MainEventsCleared => { - app.update(); + loop { + println!("Connecting to MRPIS..."); + + let mut player = match player_finder.find_active() { + Ok(player) => Player::from_player(player), + Err(err) => { + eprintln!("Couldn't find player: {:?}", err); + let wait = std::time::Duration::from_secs(1); + std::thread::sleep(wait); + continue; } - Event::RedrawRequested(_) => { - app.draw(); - } - _ => {} - } - }); + }; + + while !player.tick() {} + + println!("Disconnected from MPRIS"); + } }