use std::thread;
use crossbeam_channel::{bounded, Receiver, Sender};
use glium::glutin::event_loop::EventLoopProxy;
use log::{error, info};
use koibumi_common_sync::{
boxes::{Boxes, DEFAULT_USER_ID},
param::Params,
};
use koibumi_core::{address::Address, message};
use koibumi_node_sync::{self as node, Command as NodeCommand, Event as BmEvent, Response};
use crate::{config::Config as GuiConfig, ids::Ids, messages};
fn proxy_loop(proxy: EventLoopProxy<BmEvent>, receiver: Receiver<Receiver<BmEvent>>) {
while let Ok(receiver) = receiver.recv() {
while let Ok(event) = receiver.recv() {
if let Err(_err) = proxy.send_event(event) {
break;
}
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum State {
Idle,
Running,
Stopping,
}
pub struct Gui {
params: Params,
boxes: Option<Boxes>,
command_sender: Sender<NodeCommand>,
response_receiver: Receiver<Response>,
pub(crate) state: State,
config: GuiConfig,
tabs: crate::tab::Tabs,
messages_tab: crate::messages::Tab,
send_tab: crate::send::Tab,
identities_tab: crate::identities::Tab,
contacts_tab: crate::contacts::Tab,
subscriptions_tab: crate::subscriptions::Tab,
settings_tab: crate::settings::Tab,
pub(crate) status_tab: crate::status::Tab,
logger: crate::log::Logger,
proxy_sender: Sender<Receiver<BmEvent>>,
}
impl Gui {
pub(crate) fn new(proxy: EventLoopProxy<BmEvent>) -> Self {
let params = Params::new();
koibumi_common_sync::log::init(¶ms).unwrap_or_else(|err| {
println!("Warning: Failed to initialize logger.");
println!("{}", err);
});
let config = koibumi_common_sync::config::load(¶ms).unwrap_or_else(|err| {
error!("Failed to load config file: {}", err);
std::process::exit(1)
});
let mut boxes = match koibumi_common_sync::boxes::prepare(¶ms) {
Ok(boxes) => Some(boxes),
Err(err) => {
error!("{}", err);
None
}
};
let mut messages_tab = crate::messages::Tab::default();
messages_tab.entries = if let Some(boxes) = &mut boxes {
let entries = boxes.manager().message_list(DEFAULT_USER_ID);
if let Err(err) = entries {
error!("{}", err);
Vec::new()
} else {
let mut list = Vec::new();
let mut count = 0;
for entry in entries.unwrap() {
if !entry.read() {
count += 1;
}
list.push(crate::messages::Entry::new(entry));
}
boxes.set_unread_count(count);
list
}
} else {
Vec::new()
};
let (command_sender, response_receiver, _node_handle) = node::spawn();
const PROXY_BUFFER: usize = 0x10000;
let (proxy_sender, proxy_receiver) = bounded(PROXY_BUFFER);
thread::spawn(|| proxy_loop(proxy, proxy_receiver));
Self {
params,
boxes,
command_sender,
response_receiver,
state: State::Idle,
config: GuiConfig::default(),
tabs: crate::tab::Tabs::default(),
messages_tab,
send_tab: crate::send::Tab::default(),
identities_tab: crate::identities::Tab::default(),
contacts_tab: crate::contacts::Tab::default(),
subscriptions_tab: crate::subscriptions::Tab::default(),
settings_tab: crate::settings::Tab::new(&config),
status_tab: crate::status::Tab::default(),
logger: crate::log::Logger::default(),
proxy_sender,
}
}
pub(crate) fn set_widgets(&mut self, ui: &mut conrod_core::UiCell, ids: &mut Ids) {
use conrod_core::{
color, text, widget, Colorable, Labelable, Positionable, Sizeable, Widget,
};
let text_size = self.config.text_size();
let font_id = ui.fonts.ids().next().unwrap();
widget::Canvas::new()
.flow_down(&[
(
ids.header,
widget::Canvas::new()
.length(text_size as f64 + 16.0)
.pad_bottom(2.0),
),
(ids.body, widget::Canvas::new().pad_bottom(2.0)),
(
ids.footer,
widget::Canvas::new()
.length(text_size as f64 + 8.0)
.pad_bottom(2.0),
),
])
.set(ids.master, ui);
let label = "Start";
let width = text::line::width(label, ui.fonts.get(font_id).unwrap(), text_size as u32);
let mut button = widget::Button::new()
.top_left_with_margins_on(ids.header, 2.0, 2.0)
.w_h(width + 16.0, text_size as f64 + 12.0)
.label_font_size(text_size as u32);
match self.state {
State::Idle => {
button = button
.label("Start")
.color(color::BLUE)
.label_color(color::WHITE);
}
State::Running => {
button = button
.label("Stop")
.color(color::RED)
.label_color(color::WHITE);
}
State::Stopping => {
button = button
.label("Abort")
.color(color::LIGHT_GRAY)
.label_color(color::BLACK);
}
}
for _click in button.set(ids.start_button, ui) {
self.start_button_pushed();
}
self.tabs.set_widgets(ui, ids, &self.config);
match self.tabs.selected() {
crate::tab::Tab::Messages => {
self.messages_tab
.set_widgets(ui, ids, &self.config, &mut self.boxes);
}
crate::tab::Tab::Send => {
self.send_tab.set_widgets(
ui,
ids,
&self.config,
self.state,
&mut self.boxes,
&mut self.command_sender,
&mut self.logger,
);
}
crate::tab::Tab::Identities => {
let identities = if let Some(boxes) = &self.boxes {
boxes.user().private_identities().to_vec()
} else {
Vec::new()
};
let selected_index = if let Some(boxes) = &self.boxes {
boxes.selected_identity_index()
} else {
None
};
self.identities_tab.set_widgets(
ui,
ids,
&self.config,
&identities,
selected_index,
&mut self.boxes,
&mut self.command_sender,
&mut self.logger,
);
}
crate::tab::Tab::Contacts => {
let contacts = if let Some(boxes) = &self.boxes {
boxes.user().contacts().to_vec()
} else {
Vec::new()
};
let selected_index = if let Some(boxes) = &self.boxes {
boxes.selected_contact_index()
} else {
None
};
self.contacts_tab.set_widgets(
ui,
ids,
&self.config,
&contacts,
selected_index,
&mut self.boxes,
&mut self.command_sender,
);
}
crate::tab::Tab::Subscriptions => {
let subscriptions = if let Some(boxes) = &self.boxes {
boxes.user().subscriptions().to_vec()
} else {
Vec::new()
};
self.subscriptions_tab.set_widgets(
ui,
ids,
&self.config,
&subscriptions,
&mut self.boxes,
&mut self.command_sender,
);
}
crate::tab::Tab::Settings => {
self.settings_tab
.set_widgets(ui, ids, &self.config, self.state);
}
crate::tab::Tab::Status => {
self.status_tab.set_widgets(ui, ids, &self.config);
}
crate::tab::Tab::Log => {
self.logger.tab.set_widgets(ui, ids, &self.config);
}
}
self.logger.bar.set_widgets(ui, ids, &self.config);
}
pub(crate) fn start_button_pushed(&mut self) {
match self.state {
State::Idle => {
info!("Start");
self.logger.info("Start");
if self.boxes.is_none() {
error!("No boxes");
self.logger.error("No boxes");
return;
}
let config = self.settings_tab.create_config();
self.settings_tab.seeds_value = config
.seeds()
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" ");
self.settings_tab.max_outgoing_initiated_value =
config.max_outgoing_initiated().to_string();
self.settings_tab.max_outgoing_established_value =
config.max_outgoing_established().to_string();
self.settings_tab.own_nodes_value = config
.own_nodes()
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(" ");
if config != self.settings_tab.config {
self.settings_tab.config = config.clone();
if let Err(err) = koibumi_common_sync::config::save(&self.params, &config) {
error!("{}", err);
self.logger.error("Could not save config file");
}
}
let sender = self.command_sender.clone();
let response = {
let pool = koibumi_common_sync::node::prepare(&self.params);
if let Err(err) = pool {
error!("{}", err);
self.logger.error("Could not prepare node");
return;
}
let pool = pool.unwrap();
let users = vec![self.boxes.as_ref().unwrap().user().clone().into()];
if let Err(err) = sender.send(NodeCommand::Start(config.into(), pool, users)) {
error!("{}", err);
self.logger.error("Could not start node");
return;
}
self.response_receiver.recv()
};
if let Ok(response) = response {
let Response::Started(receiver) = response;
if let Err(err) = self.proxy_sender.send(receiver) {
error!("{}", err);
self.logger.error("Could not set BM event receiver");
return;
}
} else {
return;
}
self.state = State::Running;
}
State::Running => {
info!("Stop");
self.logger.info("Stop");
if let Err(err) = self.command_sender.send(NodeCommand::Stop) {
error!("{}", err);
return;
}
self.state = State::Stopping;
}
State::Stopping => {
info!("Abort");
self.logger.info("Abort");
if let Err(err) = self.command_sender.send(NodeCommand::Abort) {
error!("{}", err);
return;
}
}
}
}
pub(crate) fn handle_msg(
&mut self,
user_id: Vec<u8>,
address: Address,
object: message::Object,
) {
if let Some(boxes) = &mut self.boxes {
let identity = boxes.user().private_identity_by_address(&address);
if identity.is_none() {
error!("identity not found for address: {}", address);
return;
}
let identity = identity.unwrap();
match boxes.manager().insert_msg(user_id, identity, object) {
Ok(message) => {
let entry =
messages::Entry::new(koibumi_box_sync::MessageEntry::from(&message));
self.messages_tab.entries.insert(0, entry);
boxes.increment_unread_count();
}
Err(err) => {
error!("{}", err);
return;
}
}
}
}
pub(crate) fn handle_broadcast(
&mut self,
user_id: Vec<u8>,
address: Address,
object: message::Object,
) {
if let Some(boxes) = &mut self.boxes {
match boxes.manager().insert_broadcast(user_id, address, object) {
Ok(message) => {
let entry =
messages::Entry::new(koibumi_box_sync::MessageEntry::from(&message));
self.messages_tab.entries.insert(0, entry);
boxes.increment_unread_count();
}
Err(err) => {
error!("{}", err);
return;
}
}
}
}
}