summaryrefslogtreecommitdiff
path: root/client-native-gui
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2022-12-11 20:58:30 +0100
committermetamuffin <metamuffin@disroot.org>2022-12-11 20:58:30 +0100
commit86ae7283820e0f12d6d40d68d272b1593c2239e8 (patch)
treec2d97b06b730ba86faaf5b6f8ca19258a52b7742 /client-native-gui
parent43bd492ffe61dfec82c172f3eeda869809aa0932 (diff)
downloadkeks-meet-86ae7283820e0f12d6d40d68d272b1593c2239e8.tar
keks-meet-86ae7283820e0f12d6d40d68d272b1593c2239e8.tar.bz2
keks-meet-86ae7283820e0f12d6d40d68d272b1593c2239e8.tar.zst
chat (nattive gui)
Diffstat (limited to 'client-native-gui')
-rw-r--r--client-native-gui/Cargo.toml1
-rw-r--r--client-native-gui/src/chat.rs59
-rw-r--r--client-native-gui/src/main.rs117
3 files changed, 146 insertions, 31 deletions
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 {})
}));