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, +}