diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | client-native-gui/Cargo.toml | 1 | ||||
-rw-r--r-- | client-native-gui/src/chat.rs | 59 | ||||
-rw-r--r-- | client-native-gui/src/main.rs | 117 | ||||
-rw-r--r-- | client-native-lib/src/instance.rs | 6 | ||||
-rw-r--r-- | client-native-lib/src/peer.rs | 2 |
6 files changed, 151 insertions, 35 deletions
@@ -2133,6 +2133,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-std", + "clap", "client-native-lib", "crossbeam-channel", "eframe", diff --git a/client-native-gui/Cargo.toml b/client-native-gui/Cargo.toml index e917276..8ce3445 100644 --- a/client-native-gui/Cargo.toml +++ b/client-native-gui/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] client-native-lib = { path = "../client-native-lib" } +clap = { version = "4.0.29", features = ["derive"] } async-std = "1.12.0" tokio = { version = "1.21.2", features = ["full"] } env_logger = "0.8" diff --git a/client-native-gui/src/chat.rs b/client-native-gui/src/chat.rs new file mode 100644 index 0000000..dc4c2bc --- /dev/null +++ b/client-native-gui/src/chat.rs @@ -0,0 +1,59 @@ +use crate::GuiPeer; +use async_std::task::block_on; +use client_native_lib::{ + instance::Instance, + protocol::{ChatMesssage, RelayMessage}, +}; +use egui::{Key, ScrollArea, TextEdit, Ui}; +use std::{ + collections::VecDeque, + sync::{Arc, RwLock}, +}; + +pub struct Chat { + instance: Arc<Instance>, + input_line: String, + pub history: VecDeque<(Option<Arc<RwLock<GuiPeer>>>, ChatMesssage)>, +} + +impl Chat { + pub fn new(instance: Arc<Instance>) -> Self { + Chat { + instance, + input_line: "".into(), + history: VecDeque::new(), + } + } + pub fn ui(&mut self, ui: &mut Ui) { + ScrollArea::vertical().id_source("chat").show(ui, |ui| { + ui.label("this is the chat!"); + for (sender, message) in &self.history { + let sender = sender + .as_ref() + .map(|s| s.read().unwrap().display_name()) + .unwrap_or(String::from("Me")); + match message { + ChatMesssage::Text(s) => { + ui.label(&format!("{}: {}", sender, s)); + } + ChatMesssage::Image(_) => { + ui.label("<image here>"); + } + }; + } + let r = TextEdit::singleline(&mut self.input_line).show(ui).response; + if r.lost_focus() && r.ctx.input().key_down(Key::Enter) { + self.send(ChatMesssage::Text(self.input_line.to_owned())); + self.input_line = "".into(); + r.request_focus(); + } + }); + } + pub fn add(&mut self, sender: Option<Arc<RwLock<GuiPeer>>>, message: ChatMesssage) { + self.history.push_back((sender, message)); + } + pub fn send(&mut self, message: ChatMesssage) { + self.add(None, message.clone()); + block_on(self.instance.send_relay(None, RelayMessage::Chat(message))); + } +} diff --git a/client-native-gui/src/main.rs b/client-native-gui/src/main.rs index f92fa7a..11290ee 100644 --- a/client-native-gui/src/main.rs +++ b/client-native-gui/src/main.rs @@ -1,6 +1,10 @@ #![feature(box_syntax)] +pub mod chat; + use async_std::task::block_on; +use chat::Chat; +use clap::Parser; use client_native_lib::{ instance::Instance, peer::Peer, @@ -14,13 +18,12 @@ use client_native_lib::{ }; use crossbeam_channel::Sender; use eframe::egui; -use egui::{Ui, Visuals}; +use egui::{ScrollArea, Ui, Visuals}; use log::{debug, error, warn}; use std::{ collections::{HashMap, VecDeque}, fs::File, io::Write, - ops::Deref, sync::{ atomic::{AtomicBool, Ordering}, Arc, RwLock, @@ -30,6 +33,16 @@ use std::{ }; use tokio::task::JoinHandle; +#[derive(Parser)] +#[clap(about)] +/// A graphical interface to keks-meet conferences +struct Args { + #[arg(short = 'R', long, default_value = "")] + default_room_name: String, + #[arg(short = 'U', long, default_value = "alice")] + default_username: String, +} + #[tokio::main] async fn main() { env_logger::builder() @@ -38,6 +51,8 @@ async fn main() { .parse_env("LOG") .init(); + let args = Args::parse(); + let options = eframe::NativeOptions::default(); eframe::run_native( "keks-meet", @@ -47,7 +62,7 @@ async fn main() { dark_mode: true, ..Default::default() }); - Box::new(App::new()) + Box::new(App::new(args)) }), ); } @@ -58,16 +73,21 @@ enum App { Ingame(Ingame), } +#[derive(Clone)] +// TODO +#[allow(dead_code)] struct Ingame { pub instance: Arc<Instance>, pub handler: Arc<Handler>, + pub chat: Arc<RwLock<Chat>>, } -struct Handler { - peers: RwLock<HashMap<usize, GuiPeer>>, +pub struct Handler { + k: RwLock<Option<Ingame>>, + peers: RwLock<HashMap<usize, Arc<RwLock<GuiPeer>>>>, } -struct GuiPeer { +pub struct GuiPeer { peer: Arc<Peer>, resources: HashMap<String, GuiResource>, username: Option<String>, @@ -87,8 +107,8 @@ enum GuiResourceState { } impl App { - pub fn new() -> Self { - Self::Prejoin("longtest".to_string(), "blub".to_string()) + pub fn new(args: Args) -> Self { + Self::Prejoin(args.default_room_name, args.default_username) } } @@ -134,17 +154,42 @@ impl Ingame { let instance = instance.clone(); tokio::spawn(instance.receive_loop()); } - Self { instance, handler } + let k = Self { + chat: Arc::new(RwLock::new(Chat::new(instance.clone()))), + instance, + handler, + }; + *k.handler.k.write().unwrap() = Some(k.clone()); + k } - pub fn ui(&self, ui: &mut Ui) { - for peer in self.handler.peers.write().unwrap().values_mut() { - ui.collapsing(peer.display_name(), |ui| { - for resource in peer.resources.values_mut() { - resource.ui(ui, &peer.peer) + pub fn ui(&mut self, ui: &mut Ui) { + egui::SidePanel::left("chat") + .resizable(true) + .default_width(100.0) + .width_range(100.0..=1000.0) + .show_inside(ui, |ui| { + self.chat.write().unwrap().ui(ui); + ui.allocate_space(ui.available_size()); + }); + egui::CentralPanel::default().show_inside(ui, |ui| { + self.ui_user_list(ui); + }); + } + pub fn ui_user_list(&self, ui: &mut Ui) { + ScrollArea::vertical() + .id_source("user-list") + .show(ui, |ui| { + for gp in self.handler.peers.write().unwrap().values_mut() { + let mut gp = gp.write().unwrap(); + ui.collapsing(gp.display_name(), |ui| { + let peer = gp.peer.clone(); + for resource in gp.resources.values_mut() { + resource.ui(ui, &peer) + } + }); } }); - } } } impl GuiResource { @@ -190,6 +235,7 @@ impl GuiResource { impl Handler { pub fn new() -> Self { Self { + k: RwLock::new(None), peers: Default::default(), } } @@ -210,11 +256,11 @@ impl EventHandler for Handler { ) -> client_native_lib::DynFut<()> { self.peers.write().unwrap().insert( peer.id, - GuiPeer { + Arc::new(RwLock::new(GuiPeer { resources: HashMap::new(), peer: peer.clone(), username: None, - }, + })), ); Box::pin(async move {}) } @@ -233,7 +279,7 @@ impl EventHandler for Handler { info: client_native_lib::protocol::ProvideInfo, ) -> client_native_lib::DynFut<()> { if let Some(gp) = self.peers.write().unwrap().get_mut(&peer.id) { - gp.resources.insert( + gp.write().unwrap().resources.insert( info.id.clone(), GuiResource { info, @@ -250,7 +296,7 @@ impl EventHandler for Handler { id: String, ) -> client_native_lib::DynFut<()> { if let Some(gp) = self.peers.write().unwrap().get_mut(&peer.id) { - gp.resources.remove(&id); + gp.write().unwrap().resources.remove(&id); } Box::pin(async move {}) } @@ -261,13 +307,14 @@ impl EventHandler for Handler { resource: &client_native_lib::protocol::ProvideInfo, channel: client_native_lib::peer::TransportChannel, ) -> client_native_lib::DynFut<()> { - if let Some(gp) = self.peers.write().unwrap().get_mut(&peer.id) { + if let Some(gp) = self.peers.write().unwrap().get(&peer.id) { + let mut gp = gp.write().unwrap(); + let peer = gp.peer.clone(); if let Some(gr) = gp.resources.get_mut(&resource.id) { + let state = gr.state.clone(); *gr.state.write().unwrap() = GuiResourceState::Connected; match channel { client_native_lib::peer::TransportChannel::Track(track) => { - let peer = gp.peer.clone(); - let state = gr.state.clone(); tokio::task::spawn_blocking(move || { play(peer, track); *state.write().unwrap() = GuiResourceState::Available; @@ -287,10 +334,20 @@ impl EventHandler for Handler { peer: Arc<Peer>, message: &client_native_lib::protocol::RelayMessage, ) -> client_native_lib::DynFut<()> { - let mut guard = self.peers.write().unwrap(); - let p = guard.get_mut(&peer.id).unwrap(); + let guard = self.peers.read().unwrap(); + let mut p = guard.get(&peer.id).unwrap().write().unwrap(); match message.clone() { RelayMessage::Identify { username } => p.username = Some(username), + RelayMessage::Chat(message) => self + .k + .read() + .unwrap() + .as_ref() + .unwrap() + .chat + .write() + .unwrap() + .add(Some(guard.get(&peer.id).unwrap().clone()), message), _ => (), }; Box::pin(async move {}) @@ -391,16 +448,14 @@ pub fn play(peer: Arc<Peer>, track: Arc<TrackRemote>) { ) }; let proto_ctx = mpv.create_protocol_context(); + let uri = format!("keks-meet-track://{}", rid); proto_ctx.register(proto).unwrap(); - mpv.playlist_load_files(&[( - &format!("keks-meet-track://{}", rid), - libmpv::FileState::AppendPlay, - None, - )]) - .unwrap(); + mpv.playlist_load_files(&[(&uri, libmpv::FileState::AppendPlay, None)]) + .unwrap(); + mpv.command("show-text", &[&uri, "2000"]).unwrap(); block_on(track.onmute(move || { - debug!("mute"); + debug!("track muted"); let _ = exit_tx.send(()); Box::pin(async move {}) })); diff --git a/client-native-lib/src/instance.rs b/client-native-lib/src/instance.rs index 3edf50a..324c4af 100644 --- a/client-native-lib/src/instance.rs +++ b/client-native-lib/src/instance.rs @@ -120,14 +120,14 @@ impl Instance { } } - pub async fn send_relay(&self, recipient: usize, inner: RelayMessage) { - debug!("(relay) -> ({recipient}) {inner:?}"); + pub async fn send_relay(&self, recipient: Option<usize>, inner: RelayMessage) { + debug!("(relay) -> ({recipient:?}) {inner:?}"); self.conn .send .write() .await .send(ServerboundPacket::Relay { - recipient: Some(recipient), + recipient, message: self.key.encrypt( &serde_json::to_string(&RelayMessageWrapper { sender: self.my_id.read().await.expect("not ready to relay yet.."), diff --git a/client-native-lib/src/peer.rs b/client-native-lib/src/peer.rs index b271318..b3b4dd4 100644 --- a/client-native-lib/src/peer.rs +++ b/client-native-lib/src/peer.rs @@ -177,7 +177,7 @@ impl Peer { } pub async fn send_relay(&self, inner: RelayMessage) { - self.inst.send_relay(self.id, inner).await + self.inst.send_relay(Some(self.id), inner).await } pub async fn on_relay(self: &Arc<Self>, p: RelayMessage) { |