Browse Source

Integrating rust with python interface

master
Lev 3 years ago
parent
commit
8164fa9874
  1. 1
      Cargo.lock
  2. 31
      degeon/src/chat.rs
  3. 14
      degeon/src/state.rs
  4. 1
      degeon_core/Cargo.toml
  5. 53
      degeon_core/src/degeon_worker.rs
  6. 4
      degeon_py/button.py
  7. 2
      degeon_py/config.py
  8. 8
      degeon_py/degeon.py
  9. BIN
      degeon_py/degeon_core.so
  10. 4
      src/bin/worker.rs

1
Cargo.lock generated

@ -586,6 +586,7 @@ dependencies = [
"iced", "iced",
"ironforce", "ironforce",
"pyo3", "pyo3",
"rand",
"serde", "serde",
"serde_json", "serde_json",
] ]

31
degeon/src/chat.rs

@ -1,9 +1,9 @@
use iced::{Align, Button, Column, Element, Length, Row, Text, TextInput};
use iced_native::button;
use crate::gui_events::GuiEvent; use crate::gui_events::GuiEvent;
use crate::state; use crate::state;
use crate::styles::style; use crate::styles::style;
pub use degeon_core::Chat; pub use degeon_core::Chat;
use iced::{Align, Button, Column, Element, Length, Row, Text, TextInput};
use iced_native::button;
pub trait RenderableChat { pub trait RenderableChat {
/// Render header of the chat /// Render header of the chat
@ -29,10 +29,10 @@ pub trait RenderableChat {
&'a self, &'a self,
text_input_state: &'a mut iced::text_input::State, text_input_state: &'a mut iced::text_input::State,
send_button_state: &'a mut iced::button::State, send_button_state: &'a mut iced::button::State,
scroll_state: &'a mut iced::scrollable::State,
) -> Element<'a, GuiEvent>; ) -> Element<'a, GuiEvent>;
} }
impl RenderableChat for Chat { impl RenderableChat for Chat {
/// Render header of the chat /// Render header of the chat
fn header<'a>(name: String) -> Element<'a, GuiEvent> { fn header<'a>(name: String) -> Element<'a, GuiEvent> {
@ -96,20 +96,33 @@ impl RenderableChat for Chat {
&'a self, &'a self,
text_input_state: &'a mut iced::text_input::State, text_input_state: &'a mut iced::text_input::State,
send_button_state: &'a mut iced::button::State, send_button_state: &'a mut iced::button::State,
scroll_state: &'a mut iced::scrollable::State,
) -> Element<'a, GuiEvent> { ) -> Element<'a, GuiEvent> {
let pkey_clone = self.pkey.clone(); let pkey_clone = self.pkey.clone();
let msgs = self.messages.iter().filter_map(move |msg| state::view_message(msg, pkey_clone.clone())).collect(); let msgs = self
.messages
.iter()
.filter_map(move |msg| state::view_message(msg, pkey_clone.clone()))
.collect();
Column::new() Column::new()
.align_items(Align::End) .align_items(Align::End)
.height(Length::Fill) .height(Length::Fill)
.width(Length::FillPortion(4)) .width(Length::FillPortion(4))
.push(Self::header(self.profile.name.clone())) .push(Self::header(self.profile.name.clone()))
.push( .push(
Column::with_children(msgs) iced::Container::new(
.padding(20) iced::Scrollable::new(scroll_state)
.spacing(10) .push(
.align_items(Align::End) Column::with_children(msgs)
.height(Length::FillPortion(9)), .padding(20)
.spacing(10)
.align_items(Align::End),
)
.align_items(Align::End)
.width(Length::Fill),
)
.align_y(Align::End)
.height(Length::FillPortion(9)),
) )
.spacing(10) .spacing(10)
.push(Self::send_field( .push(Self::send_field(

14
degeon/src/state.rs

@ -61,6 +61,8 @@ pub struct DegeonApp {
name_input_state: iced::text_input::State, name_input_state: iced::text_input::State,
/// Button on the profile screen /// Button on the profile screen
profile_done_button_state: iced::button::State, profile_done_button_state: iced::button::State,
/// Scroll state
scroll: iced::scrollable::State,
} }
impl DegeonApp { impl DegeonApp {
@ -94,6 +96,7 @@ impl DegeonApp {
selected_chat: usize, selected_chat: usize,
send_button_state: &'a mut button::State, send_button_state: &'a mut button::State,
text_input_state: &'a mut iced::text_input::State, text_input_state: &'a mut iced::text_input::State,
scroll_state: &'a mut iced::scrollable::State,
) -> Element<'a, GuiEvent> { ) -> Element<'a, GuiEvent> {
if selected_chat >= chats.len() { if selected_chat >= chats.len() {
Text::new("No chat") Text::new("No chat")
@ -102,7 +105,7 @@ impl DegeonApp {
.width(Length::FillPortion(4)) .width(Length::FillPortion(4))
.into() .into()
} else { } else {
chats[selected_chat].view(text_input_state, send_button_state) chats[selected_chat].view(text_input_state, send_button_state, scroll_state)
} }
} }
} }
@ -115,6 +118,7 @@ impl DegeonApp {
send_button_state, send_button_state,
text_input_state, text_input_state,
preview_button_states, preview_button_states,
scroll: scroll_state,
.. ..
} = self; } = self;
Row::new() Row::new()
@ -129,6 +133,7 @@ impl DegeonApp {
*selected_chat, *selected_chat,
send_button_state, send_button_state,
text_input_state, text_input_state,
scroll_state,
)) ))
.height(Length::Fill) .height(Length::Fill)
.into() .into()
@ -182,8 +187,12 @@ impl Application for DegeonApp {
fn new(_: ()) -> (Self, iced::Command<GuiEvent>) { fn new(_: ()) -> (Self, iced::Command<GuiEvent>) {
let mut data = Degeon::restore_from_file("".to_string()).unwrap(); let mut data = Degeon::restore_from_file("".to_string()).unwrap();
data.chats = vec![Chat::example(1, &data.keys), Chat::example(2, &data.keys)]; if data.chats.is_empty() {
data.chats = vec![Chat::example(1, &data.keys), Chat::example(2, &data.keys)];
}
data.send_multicast(ProtocolMsg::Ping).unwrap(); data.send_multicast(ProtocolMsg::Ping).unwrap();
let mut scroll: iced::scrollable::State = Default::default();
scroll.scroll_to(1., iced::Rectangle::with_size(iced::Size::ZERO), iced::Rectangle::with_size(iced::Size::UNIT));
let st = DegeonApp { let st = DegeonApp {
screen: if data.profile.name.is_empty() { screen: if data.profile.name.is_empty() {
AppScreen::ProfileEditor AppScreen::ProfileEditor
@ -197,6 +206,7 @@ impl Application for DegeonApp {
preview_button_states: vec![Default::default(), Default::default()], preview_button_states: vec![Default::default(), Default::default()],
name_input_state: Default::default(), name_input_state: Default::default(),
profile_done_button_state: Default::default(), profile_done_button_state: Default::default(),
scroll,
}; };
(st, iced::Command::none()) (st, iced::Command::none())
} }

1
degeon_core/Cargo.toml

@ -13,6 +13,7 @@ serde_json = "1.0.72"
futures = "0.3.18" futures = "0.3.18"
chrono = "0.4.19" chrono = "0.4.19"
iced = { version = "0.3.0", features = ["glow"] } iced = { version = "0.3.0", features = ["glow"] }
rand = "0.8.4"
[dependencies.pyo3] [dependencies.pyo3]
version = "0.14.0" version = "0.14.0"

53
degeon_core/src/degeon_worker.rs

@ -42,7 +42,7 @@ fn get_initialized_ironforce() -> (Arc<Mutex<IronForce>>, Keys) {
let ironforce = IronForce::from_file("".to_string()).unwrap(); let ironforce = IronForce::from_file("".to_string()).unwrap();
let keys = ironforce.keys.clone(); let keys = ironforce.keys.clone();
println!("ID: {}", keys.get_public().get_short_id()); println!("ID: {}", keys.get_public().get_short_id());
let (_thread, ironforce) = ironforce.launch_main_loop(1000); let (_thread, ironforce) = ironforce.launch_main_loop(270);
(ironforce, keys) (ironforce, keys)
} }
@ -213,27 +213,44 @@ impl Degeon {
#[pymethods] #[pymethods]
impl Degeon { impl Degeon {
/// Create a new text message and send it /// Create a new text message and send it (in thread)
pub fn send_text_message(&self, text: String, chat_i: usize) -> PyResult<()> { ///
self.send_message( /// `text` is the content of the message,
ProtocolMsg::NewMessage(DegMessage::new_text(text, &self.keys.get_public())), /// `chat_i` is the index of the chat in the list
&self.chats[chat_i].pkey, pub fn send_text_message(&mut self, text: String, chat_i: usize) {
) let msg = DegMessage::new_text(text, &self.keys.get_public());
.map_err(|e| { self.chats[chat_i].messages.push(msg.clone());
pyo3::exceptions::PyTypeError::new_err(format!("There was an error in Rust: {:?}", e)) let cloned_self = self.clone();
}) std::thread::spawn(move || {
cloned_self
.send_message(
ProtocolMsg::NewMessage(msg),
&cloned_self.chats[chat_i].pkey,
)
.map_err(|e| println!("There was an error in Rust: {:?}", e))
});
} }
/// Handle one message /// Handle one message
pub fn handling_loop_iteration(&mut self) { pub fn handling_loop_iteration(&mut self) {
let event = self.read_message_and_create_event(); let event = self.read_message_and_create_event();
if let Some(event) = event { if let Some(event) = event {
self.process_event(&event, true).unwrap_or_else(|e| println!("Error: {:?}", e)); self.process_event(&event, true)
.unwrap_or_else(|e| println!("Error: {:?}", e));
} }
} }
/// Get length of the IF's message queue
///
/// If IF worker is locked, returns 0
pub fn message_queue_len(&self) -> usize { pub fn message_queue_len(&self) -> usize {
self.ironforce.lock().unwrap().messages.len() self.ironforce.try_lock().map(|r| r.messages.len()).unwrap_or(0)
}
/// Check if the message was written by the current user (since `PublicKey` isn't a python type).
/// Returns True if the author of the message is the current user
pub fn check_message_ownership(&self, message: &DegMessage) -> bool {
message.sender == self.keys.get_public()
} }
} }
@ -293,6 +310,7 @@ impl Degeon {
} }
impl Degeon { impl Degeon {
/// Get one message from the IF message queue and process it, resulting in an event
pub fn read_message_and_create_event(&self) -> Option<GuiEvent> { pub fn read_message_and_create_event(&self) -> Option<GuiEvent> {
let msg_raw = self.ironforce.lock().unwrap().read_message(); let msg_raw = self.ironforce.lock().unwrap().read_message();
let msg = msg_raw let msg = msg_raw
@ -317,7 +335,7 @@ impl Degeon {
} }
match msg { match msg {
Some(Some(event)) => Some(event), Some(Some(event)) => Some(event),
_ => None _ => None,
} }
} }
} }
@ -326,10 +344,19 @@ impl Stream for Degeon {
type Item = GuiEvent; type Item = GuiEvent;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if self.ironforce.try_lock().is_err() {
return Poll::Ready(Some(GuiEvent::None));
}
let timestamp_0 = std::time::Instant::now(); let timestamp_0 = std::time::Instant::now();
let msg = self.read_message_and_create_event(); let msg = self.read_message_and_create_event();
if timestamp_0.elapsed() < std::time::Duration::from_millis(5) { if timestamp_0.elapsed() < std::time::Duration::from_millis(5) {
std::thread::sleep(std::time::Duration::from_millis(5)); std::thread::sleep(std::time::Duration::from_millis(5));
if rand::random::<bool>() {
return Poll::Pending;
}
}
if timestamp_0.elapsed() > std::time::Duration::from_millis(800) {
println!("Poll_next took {:?}", timestamp_0.elapsed());
} }
Poll::Ready(Some(msg.unwrap_or(GuiEvent::None))) Poll::Ready(Some(msg.unwrap_or(GuiEvent::None)))
} }

4
degeon_py/button.py

@ -87,7 +87,7 @@ class Button:
# if this is a mouse event # if this is a mouse event
if event.type in [pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN]: if event.type in [pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, pygame.MOUSEBUTTONDOWN]:
# if the mouse event is inside the button # if the mouse event is inside the button
if self.top_left[0] <= event.pos[0] < self.bottom and self.top_left[1] <= event.pos[1] < self.right: if self.top_left[0] <= event.pos[0] < self.right and self.top_left[1] <= event.pos[1] < self.bottom:
if event.type == pygame.MOUSEBUTTONUP: if event.type == pygame.MOUSEBUTTONUP:
self.is_pressed = False self.is_pressed = False
return True return True
@ -96,4 +96,6 @@ class Button:
self.is_pressed = True self.is_pressed = True
elif event.type == pygame.MOUSEMOTION: elif event.type == pygame.MOUSEMOTION:
self.is_hovered = True self.is_hovered = True
else:
self.is_hovered = False
return False return False

2
degeon_py/config.py

@ -3,7 +3,7 @@ import pygame
pygame.init() pygame.init()
# Fontss # Fonts
font = pygame.font.Font('CRC35.otf', 32) font = pygame.font.Font('CRC35.otf', 32)
font_large = pygame.font.Font('CRC35.otf', 50) font_large = pygame.font.Font('CRC35.otf', 50)

8
degeon_py/degeon.py

@ -55,7 +55,8 @@ class Degeon:
""" """
Do all the necessary Rust work Do all the necessary Rust work
""" """
pass # todo while self.core.message_queue_len():
self.core.handling_loop_iteration()
def tick(self): def tick(self):
""" """
@ -78,7 +79,10 @@ class Degeon:
else: else:
self.active_chat = None self.active_chat = None
if self.active_chat is not None: if self.active_chat is not None:
self.active_chat.process_event(event) # If the result is a string, it's a message
result: typing.Optional[str] = self.active_chat.process_event(event)
if result:
self.core.send_text_message(result, self.chat_selector.active_chat)
def main_loop(self, screen: pygame.Surface): def main_loop(self, screen: pygame.Surface):
""" """

BIN
degeon_py/degeon_core.so

Binary file not shown.

4
src/bin/worker.rs

@ -8,7 +8,7 @@ fn main() -> IFResult<()> {
"Our public key: {}", "Our public key: {}",
base64::encode(if_keys.get_public().to_vec().as_slice()) base64::encode(if_keys.get_public().to_vec().as_slice())
); );
let (_thread, if_mutex) = ironforce.launch_main_loop(100); let (_thread, if_mutex) = ironforce.launch_main_loop(50);
let stdin = std::io::stdin(); let stdin = std::io::stdin();
let if_mutex_clone = if_mutex.clone(); let if_mutex_clone = if_mutex.clone();
let if_keys_clone = if_keys.clone(); let if_keys_clone = if_keys.clone();
@ -19,7 +19,7 @@ fn main() -> IFResult<()> {
String::from_utf8(msg.get_decrypted(&if_keys_clone).unwrap()).unwrap() String::from_utf8(msg.get_decrypted(&if_keys_clone).unwrap()).unwrap()
); );
} }
std::thread::sleep(std::time::Duration::from_millis(300)) std::thread::sleep(std::time::Duration::from_millis(200))
}); });
loop { loop {
let mut buf = String::new(); let mut buf = String::new();

Loading…
Cancel
Save