diff --git a/Cargo.lock b/Cargo.lock index fc9f2cb..cbfeabc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,6 +894,10 @@ dependencies = [ name = "deckui" version = "0.1.0" dependencies = [ + "anyhow", + "env_logger", + "lib", + "log", "slint", "slint-build", ] diff --git a/deckapp/src/main.rs b/deckapp/src/main.rs index 2f83540..eee7641 100644 --- a/deckapp/src/main.rs +++ b/deckapp/src/main.rs @@ -1,4 +1,6 @@ -use lib::{start_client, start_server, ServerMessage, ClientMessage}; +use std::{process::Command, string::FromUtf8Error, thread, time::Duration}; + +use lib::{tcp::start_server, ClientMessage, ServerMessage}; use log::info; fn main() { @@ -7,11 +9,53 @@ fn main() { ); let (sender_s, receiver_c) = start_server().unwrap(); - let (sender_c, receiver_s) = start_client().unwrap(); - - sender_s.send(ServerMessage::NowPlaying("Hello".to_string())).unwrap(); - sender_c.send(ClientMessage::PlayPause).unwrap(); - loop{ + let mut i = 0; + loop { + i += 1; + if i == 50 { + i = 0; + sender_s + .send(ServerMessage::NowPlaying(get_np().unwrap())) + .unwrap(); + } + if receiver_c.is_empty() { + thread::sleep(Duration::from_millis(200)); + continue; + } + let msg = receiver_c.recv().unwrap(); + info!("[Client] : {msg:?}"); + + match msg { + ClientMessage::Next => { + Command::new("playerctl").arg("next").spawn().unwrap(); + thread::sleep(Duration::from_millis(200)); + sender_s + .send(ServerMessage::NowPlaying(get_np().unwrap())) + .unwrap(); + } + ClientMessage::PlayPause => { + Command::new("playerctl").arg("play-pause").spawn().unwrap(); + } + ClientMessage::Previous => { + Command::new("playerctl").arg("previous").spawn().unwrap(); + thread::sleep(Duration::from_millis(200)); + sender_s + .send(ServerMessage::NowPlaying(get_np().unwrap())) + .unwrap(); + } + }; } } + +fn get_np() -> Result { + let output = Command::new("playerctl") + .arg("metadata") + .arg("-f") + .arg("{{title}} - {{artist}}") + .output() + .unwrap() + .stdout; + let title = String::from_utf8(output)?; + Ok(title.trim().to_string()) +} diff --git a/deckui/Cargo.toml b/deckui/Cargo.toml index 15570f5..00cd158 100644 --- a/deckui/Cargo.toml +++ b/deckui/Cargo.toml @@ -14,6 +14,12 @@ path = "src/main.rs" name = "deckui" [dependencies] +lib = {path = "../lib"} + +anyhow = "1.0" +log = "0.4" +env_logger = "0.10" + slint = "1.0" [build-dependencies] diff --git a/deckui/src/main.rs b/deckui/src/main.rs index 58516fc..4081c3a 100644 --- a/deckui/src/main.rs +++ b/deckui/src/main.rs @@ -1,19 +1,68 @@ +use std::{net::IpAddr, str::FromStr, thread, time::Duration, process::exit}; + +use lib::{tcp::start_client, ServerMessage}; +use log::info; + slint::include_modules!(); fn main() -> Result<(), slint::PlatformError> { + + env_logger::init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let mut retries = 1; + let (sender_co, receiver_s) = loop{ + let res = start_client(IpAddr::from_str("192.168.0.101").unwrap()); + if let Ok((s,r)) = res{ + break (s,r) + } + println!("Failde to connect retrying in {} ms", 200*retries); + thread::sleep(Duration::from_millis(200*retries)); + retries*=5; + if retries >= 500 { + println!("Could not connect to server exiting..."); + exit(1); + } + }; + + info!("Connected to Server"); + let ui = AppWindow::new()?; - let ui_handle = ui.as_weak(); + let sender_c = sender_co.clone(); ui.global::().on_play_pause(move || { - ui_handle.unwrap().set_now_playing("a".into()); + sender_c.send(lib::ClientMessage::PlayPause).unwrap(); }); + let sender_c = sender_co.clone(); ui.global::().on_next(move || { + sender_c.send(lib::ClientMessage::Next).unwrap(); }); + let sender_c = sender_co.clone(); ui.global::().on_previous(move || { + sender_c.send(lib::ClientMessage::Previous).unwrap(); }); + let ui_handle = ui.as_weak(); + thread::spawn(move || { + loop { + if receiver_s.is_empty(){ + thread::sleep(Duration::from_millis(200)); + continue; + } + let msg = receiver_s.recv().unwrap(); + info!("Received : {msg:?}"); + match msg { + ServerMessage::NowPlaying(title) => ui_handle.upgrade_in_event_loop(move |handle| handle.set_now_playing(title.into())).unwrap(), + + }; + + } + }); + + info!("Launching UI"); ui.run() } diff --git a/deckui/ui/appwindow.slint b/deckui/ui/appwindow.slint index d717972..402d695 100644 --- a/deckui/ui/appwindow.slint +++ b/deckui/ui/appwindow.slint @@ -5,7 +5,7 @@ export { MediaLogic } export component AppWindow inherits Window { background: white; - in-out property now_playing: "test"; + in-out property now_playing: "Loading..."; VerticalLayout { alignment: center; @@ -15,8 +15,9 @@ export component AppWindow inherits Window { horizontal-alignment: center; color: black; text: root.now_playing; - font-size:50px; + font-size:35px; font-weight: 600; + wrap: word-wrap; } } diff --git a/deckui/ui/widgets/mediacontrols.slint b/deckui/ui/widgets/mediacontrols.slint index 5a5b9a7..cc5ac85 100644 --- a/deckui/ui/widgets/mediacontrols.slint +++ b/deckui/ui/widgets/mediacontrols.slint @@ -14,6 +14,9 @@ export component MediaControls inherits Clickable { width: 450px; Clickable { icon: @image-url("../../icons/back.png"); + action => { + MediaLogic.previous(); + } } Clickable { @@ -25,6 +28,9 @@ export component MediaControls inherits Clickable { } Clickable { icon: @image-url("../../icons/skip.png"); + action => { + MediaLogic.next(); + } } diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 1d13475..3ec8c31 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,30 +1,8 @@ -use std::{ - io::{self, Read, Write}, - net::{TcpListener, TcpStream}, - result::Result::Ok, - thread, - time::Duration, -}; +pub mod tcp; +use std::fmt::{Debug, Display}; -use anyhow::Result; -use crossbeam::channel::{self, Receiver, Sender}; -use log::{error, info}; -use rmp_serde::{Deserializer, Serializer}; use serde::{Deserialize, Serialize}; -pub fn create_listener(address: &str, port: u16) -> TcpListener { - TcpListener::bind((address, port)).unwrap_or_else(|e| { - if e.kind() == io::ErrorKind::AddrInUse { - info!("Port {} is already being used by another program", port); - std::process::exit(1); - } else if e.kind() == io::ErrorKind::AddrNotAvailable { - info!("Address {} not available", address); - std::process::exit(1); - } else { - panic!("{:?}", e); - } - }) -} #[derive(Serialize, Deserialize, Debug)] pub enum ClientMessage { @@ -38,103 +16,16 @@ pub enum ServerMessage { NowPlaying(String), } -pub fn start_server() -> Result<(Sender, Receiver)> { - let (sender_c, reciever_c) = channel::bounded(4); - let (sender_s, reciever_s) = channel::bounded(4); - - let server = create_listener("0.0.0.0", 6487); - info!("Listening on port {}", 6487); - server.set_nonblocking(true)?; - - thread::spawn(move || { - for stream_result in server.incoming() { - match stream_result { - Ok(stream) => { - let reciever_s = reciever_s.clone(); - let sender_c = sender_c.clone(); - - thread::spawn(move || { - if let Err(error) = handle_connection(stream, reciever_s, sender_c,"Client") { - error!("error while handling stream: {}", error); - } - }) - } // Spawn a new thread, ignore the return value because we don't need to join threads - _ => continue, - }; - } - }); - - Ok((sender_s, reciever_c)) +pub enum Origin { + Server, + Client, } - - - -pub fn start_client() -> Result<(Sender, Receiver)> { - let (sender_c, reciever_c) = channel::bounded(4); - let (sender_s, reciever_s) = channel::bounded(4); - - info!("Connecting"); - let server = TcpStream::connect("127.0.0.1:6487")?; - info!("Connected successfully to {}", "127.0.0.1:6487"); - server.set_nonblocking(true)?; - - thread::spawn(move || { - handle_connection(server, reciever_c, sender_s, "Server") }); - - Ok((sender_c, reciever_s)) -} - - - - -fn handle_connection( - mut stream: TcpStream, - reciever: Receiver, - sender: Sender, - origin: &str, -) -> Result<()> -where R : Serialize -{ - loop { - thread::sleep(Duration::from_millis(200)); - if !reciever.is_empty() { - let msg: R = reciever.recv()?; - let mut msg_buf = Vec::new(); - msg.serialize(&mut Serializer::new(&mut msg_buf))?; - - stream.write(&msg_buf)?; +impl Display for Origin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Origin::Client => write!(f, "Client"), + Origin::Server => write!(f, "Server"), } - handle_read(&mut stream, origin)?; } } - -fn handle_read(stream: &mut TcpStream, origin: &str) -> Result<()> { - let mut byte_read = 0; - let mut read_buff = [0u8; 4096]; - - if byte_read == 0 { - byte_read = match stream.read(&mut read_buff) { - Ok(0) => return Ok(()), // Socket closed - Ok(n) => n, - Err(e) if e.kind() == io::ErrorKind::WouldBlock => 0, // If there is no data, return 0 bytes written - Err(_) => return Ok(()), - }; - } - - // Write data from tunnel to stream - if byte_read > 0 { - info!("[{origin}] {:?}", &read_buff[0..byte_read]); - - if origin == "Server" { - let a: ServerMessage = rmp_serde::from_slice(&read_buff[0..byte_read]).unwrap(); - info!("{:?}", a); - } else { - let a: ClientMessage = rmp_serde::from_slice(&read_buff[0..byte_read]).unwrap(); - info!("{:?}", a); - - } - //sender_c.send(msg); - } - Ok(()) -} diff --git a/lib/src/tcp/mod.rs b/lib/src/tcp/mod.rs new file mode 100644 index 0000000..490ce2c --- /dev/null +++ b/lib/src/tcp/mod.rs @@ -0,0 +1,130 @@ +use crate::*; + +use std::{ + fmt::Debug, + io::{self, Read, Write}, + net::{IpAddr, SocketAddr, TcpListener, TcpStream}, + result::Result::Ok, + thread, + time::Duration, +}; + +use anyhow::Result; +use crossbeam::channel::{self, Receiver, Sender}; +use log::{error, info}; +use rmp_serde::Serializer; +use serde::{Deserialize, Serialize}; + +const PORT: u16 = 6478; + +pub fn create_listener(address: &str, port: u16) -> TcpListener { + TcpListener::bind((address, port)).unwrap_or_else(|e| { + if e.kind() == io::ErrorKind::AddrInUse { + info!("Port {} is already being used by another program", port); + std::process::exit(1); + } else if e.kind() == io::ErrorKind::AddrNotAvailable { + info!("Address {} not available", address); + std::process::exit(1); + } else { + panic!("{:?}", e); + } + }) +} + +fn handle_connection( + mut stream: TcpStream, + reciever: Receiver, + sender: Sender, + origin: Origin, +) -> Result<()> +where + R: Serialize + Debug, + S: for<'a> Deserialize<'a> + Debug, +{ + stream.set_nonblocking(true)?; + loop { + thread::sleep(Duration::from_millis(200)); + while !reciever.is_empty() { + let msg: R = reciever.recv()?; + let mut msg_buf = Vec::new(); + msg.serialize(&mut Serializer::new(&mut msg_buf))?; + info!("Sent to {origin} : {msg:?}"); + stream.write(&msg_buf)?; + } + handle_read(&mut stream, &origin, &sender)?; + } +} + +fn handle_read(stream: &mut TcpStream, origin: &Origin, sender: &Sender) -> Result<()> +where + S: for<'a> Deserialize<'a> + Debug, +{ + let mut byte_read = 0; + let mut read_buff = [0u8; 4096]; + + if byte_read == 0 { + byte_read = match stream.read(&mut read_buff) { + Ok(0) => return Ok(()), // Socket closed + Ok(n) => n, + Err(e) if e.kind() == io::ErrorKind::WouldBlock => 0, // If there is no data, return 0 bytes written + Err(_) => return Ok(()), + }; + } + + if byte_read > 0 { + let msg: S = rmp_serde::from_slice(&read_buff[0..byte_read]).unwrap(); + info!("[{origin}] {:?}", msg); + + sender.send(msg).expect("Could not send"); + } + Ok(()) +} + + + +pub fn start_server() -> Result<(Sender, Receiver)> { + let (sender_c, reciever_c) = channel::bounded(4); + let (sender_s, reciever_s) = channel::bounded(4); + + let server = create_listener("0.0.0.0", PORT); + info!("Listening on port {}", PORT); + server.set_nonblocking(true)?; + + thread::spawn(move || { + for stream_result in server.incoming() { + match stream_result { + Ok(stream) => { + let reciever_s = reciever_s.clone(); + let sender_c = sender_c.clone(); + + thread::spawn(move || { + if let Err(error) = + handle_connection(stream, reciever_s, sender_c, Origin::Client) + { + error!("error while handling stream: {}", error); + return; + } + }) + } // Spawn a new thread, ignore the return value because we don't need to join threads + _ => continue, + }; + } + }); + + Ok((sender_s, reciever_c)) +} + +pub fn start_client(ip: IpAddr) -> Result<(Sender, Receiver)> { + let (sender_c, reciever_c) = channel::bounded(4); + let (sender_s, reciever_s) = channel::bounded(4); + + let serv_addr = SocketAddr::new(ip, PORT); + + let server = TcpStream::connect(serv_addr)?; + info!("Connected successfully to {}", serv_addr); + + thread::spawn(move || handle_connection(server, reciever_c, sender_s, Origin::Server)); + + Ok((sender_c, reciever_s)) +} + diff --git a/runcc.yml b/runcc.yml index 0f7243d..bba3b50 100644 --- a/runcc.yml +++ b/runcc.yml @@ -4,5 +4,5 @@ commands: deckui: | cargo watch -d 2 -w deckui -- sh ./buildui.sh deckapp: | - cargo watch -d 2 -w deckapp -w lib -x "run -p deckapp" + cargo watch -d 2 -w deckapp -w lib -x "run --release -p deckapp"