Skip to content

Commit

Permalink
sync -2 s-1 split after
Browse files Browse the repository at this point in the history
  • Loading branch information
wychlw committed Oct 11, 2024
1 parent 9f2e3ea commit 192e300
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 61 deletions.
26 changes: 26 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ inventory = "0.3.15"
eframe = "0.28.1"
egui_extras = "0.28.1"
termwiz = "0.22.0"
interprocess = "2.2.1"

[toolchain]
channel = "nightly"
Expand Down
5 changes: 5 additions & 0 deletions python/tester/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@
__doc__ = None
if hasattr(tester, "__all__"):
__all__ = tester.__all__

if hasattr(globals(), "__virt__"):
ui.__init_sub_virt__(__virt__)
else:
ui.__init_sub_virt__("not_in_GUI_env")
2 changes: 1 addition & 1 deletion src/pythonapi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub mod util;

pub mod sdwirec;

pub mod pylogger;
mod pylogger;
pub mod asciicast;

pub mod deansi;
Expand Down
18 changes: 13 additions & 5 deletions src/ui/code_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use std::{fs::File, io::Write};

use eframe::egui::{Context, Id, ScrollArea, TextEdit, TextStyle, Ui, Window};
use egui_extras::syntax_highlighting::{highlight, CodeTheme};
use interprocess::local_socket::Stream;

use crate::{err, impl_sub_window};
use crate::{err, impl_sub_window, info};

use super::{main::SubWindow, pyenv::PyEnv};
use super::{main::SubWindow, pyenv::PyEnv, util::get_main_virt};

pub struct CodeEditor {
code: String,
Expand All @@ -21,9 +22,10 @@ from tester import *
from tester.ui import *
print('Hello, world!')
".to_string(),
"
.to_string(),
save_to: "".to_string(),
pyenv: PyEnv::default(),
pyenv: PyEnv::build(&get_main_virt()),
}
}
}
Expand Down Expand Up @@ -101,11 +103,17 @@ impl CodeEditor {

impl SubWindow for CodeEditor {
fn show(&mut self, ctx: &Context, title: &str, id: &Id, open: &mut bool) {
let window = Window::new(title).id(id.to_owned()).open(open).resizable([true, true]);
let window = Window::new(title)
.id(id.to_owned())
.open(open)
.resizable([true, true]);
window.show(ctx, |ui| {
self.show(ui);
});
}
fn on_ipc(&mut self, msg: &str, _conn: &mut Stream) {
info!("CodeEditor received IPC message: {}", msg);
}
}

impl_sub_window!(CodeEditor, "CodeEditor");
89 changes: 89 additions & 0 deletions src/ui/ipc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//! IPC between windows and sub-windows.
//!
//! As the program may run in multiple processes, or in same process but different address space,
//! we need a way to communicate between windows and sub-windows.
use std::{
error::Error,
io::{BufReader, ErrorKind},
};

use interprocess::local_socket::{
traits::ListenerExt, GenericNamespaced, Listener, ListenerNonblockingMode, ListenerOptions,
ToNsName,
};
use serde::{Deserialize, Serialize};
use serde_json::from_reader;

use crate::info;

use super::{
main::MyApp,
util::get_main_virt,
};

#[derive(Serialize, Deserialize)]
pub struct WindowIpcMessage {
pub window_id: u64,
pub message: String,
}

pub fn init_ipc() -> Result<Listener, Box<dyn Error>> {
let sock_name = get_sock_name(get_main_virt().to_owned(), None);
let sock_name = sock_name.to_ns_name::<GenericNamespaced>()?;

let opts = ListenerOptions::new()
.name(sock_name)
.nonblocking(ListenerNonblockingMode::Both);

let listener = opts.create_sync()?;
Ok(listener)
}

impl MyApp {
pub(super) fn handle_ipc(&mut self) {
for m in self.listener.incoming() {
match m {
Err(e) if e.kind() == ErrorKind::WouldBlock => {
info!("IPC would block");
break;
}
Err(e) => {
info!("IPC error: {}", e);
continue;
}
Ok(m) => {
info!("IPC message received");
let mut reader = BufReader::new(m);
let msg: WindowIpcMessage = match from_reader(&mut reader) {
Ok(m) => m,
Err(e) => {
info!("IPC message decode error: {}", e);
continue;
}
};
info!(
"Received IPC message from window {}: {}",
msg.window_id, msg.message
);
let conn = reader.get_mut();
for w in self.sub_windows.iter_mut() {
if w.id.value() == msg.window_id {
w.window.on_ipc(&msg.message, conn);
}
}
}
}
}
}
}

pub fn get_sock_name(base_name: String, window_id: Option<u64>) -> String {
if let Some(id) = window_id {
format!("{}_{}.sock", base_name, id)
} else {
base_name + ".sock"
}
}


18 changes: 13 additions & 5 deletions src/ui/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
//! Main UI render for the APP
use std::{
error::Error, thread::sleep, time::Duration,
};
use std::{error::Error, thread::sleep, time::Duration};

use eframe::{
egui::{Context, Id, SidePanel, Ui, ViewportBuilder},
run_native, App, Frame, NativeOptions,
};
use interprocess::local_socket::{Listener, Stream};

use crate::{info, util::anybase::AnyBase};

use super::ipc::init_ipc;

/// Main UI struct
///
/// NOTICE! NOTICE! This will block the main thread. If you have any other tasks to do, please run them in a separate thread.
Expand Down Expand Up @@ -44,10 +45,11 @@ pub struct SubWindowHolder {
pub open: bool,
}

struct MyApp {
pub struct MyApp {
sub_window_creator: Vec<Box<DynSubWindowCreator>>, // We ensure that the sub windows only work in the main thread
sub_windows: Vec<SubWindowHolder>,
pub(super) sub_windows: Vec<SubWindowHolder>,
sub_window_idx: usize,
pub(super) listener: Listener,
}

impl Default for MyApp {
Expand All @@ -62,6 +64,7 @@ impl Default for MyApp {
sub_window_creator,
sub_windows: Vec::new(),
sub_window_idx: 0,
listener: init_ipc().unwrap(),
}
}
}
Expand Down Expand Up @@ -102,6 +105,7 @@ impl MyApp {
impl App for MyApp {
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
let _ = frame;
self.handle_ipc();
SidePanel::right("SubWindow Panel")
.default_width(200.0)
.show(ctx, |ui| {
Expand All @@ -119,6 +123,10 @@ pub trait SubWindow: AnyBase {
/// Show the window, this will be called every frame. Your window is identified by the `id` parameter.
/// However, that doesn't mean you should change the title, as this contains the window number, useful for the user.
fn show(&mut self, ctx: &Context, title: &str, id: &Id, open: &mut bool);

/// For IPC, this will be called when the IPC message is received.
/// The message is a string, you can use it as you like.
fn on_ipc(&mut self, msg: &str, stream: &mut Stream);
}

#[doc(hidden)]
Expand Down
19 changes: 14 additions & 5 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
//! Parts to handle the UI render part
//!
//!
//! If only use CLI feats, this part can be ignored;
//! however, GUI part may need this part show what's going on.
//! Or, use to create needle for GUI part.
//!
//!
pub mod main;
pub mod cli_hooker;
pub mod code_editor;
pub mod main;
pub mod pyenv;
pub mod terminal;
pub mod cli_hooker;
pub mod ui_cli_exec;
pub mod util;
pub mod ipc;

mod test_window;

use pyo3::{types::{PyModule, PyModuleMethods}, Bound, PyResult};
use pyo3::{
types::{PyModule, PyModuleMethods},
wrap_pyfunction, Bound, PyResult,
};
use ui_cli_exec::UiExec;
use util::__init_sub_virt__;

pub fn register_ui(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
let m = PyModule::new_bound(parent_module.py(), "ui")?;
m.add_class::<UiExec>()?;

m.add_function(wrap_pyfunction!(__init_sub_virt__, &m)?)?;

parent_module.add_submodule(&m)?;
Ok(())
}
6 changes: 3 additions & 3 deletions src/ui/pyenv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ pub struct PyEnv {
locals: Py<PyDict>,
}

impl Default for PyEnv {
fn default() -> Self {
impl PyEnv {
pub fn build(virt_info: &str) -> Self {
static GLOBALS: LazyLock<Py<PyDict>> = LazyLock::new(|| {
prepare_freethreaded_python();
Python::with_gil(|py| unsafe {
Expand All @@ -29,7 +29,7 @@ impl Default for PyEnv {
prepare_freethreaded_python();
Python::with_gil(|py| {
let globals = GLOBALS.clone_ref(py);
globals.bind(py).set_item("__virt__", 1).unwrap(); // Force to copy globals dict, otherwise drop one PyEnv will affect others
globals.bind(py).set_item("__virt__", virt_info).unwrap(); // Force to copy globals dict, otherwise drop one PyEnv will affect others
let locals = unsafe {
let mptr = globals.as_ptr();
Py::from_owned_ptr(py, mptr)
Expand Down
5 changes: 4 additions & 1 deletion src/ui/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use std::{
use eframe::egui::{
scroll_area::ScrollBarVisibility, Context, FontId, Id, RichText, ScrollArea, Ui, Window,
};
use interprocess::local_socket::Stream;

use crate::{consts::DURATION, impl_sub_window};
use crate::{consts::DURATION, impl_sub_window, info};

use super::main::SubWindow;

Expand Down Expand Up @@ -204,6 +205,8 @@ impl SubWindow for Terminal {
window.show(ctx, |ui| {
self.show(ui);
});
}fn on_ipc(&mut self, msg: &str, _conn: &mut Stream) {
info!("Terminal got message: {}", msg);
}
}

Expand Down
Loading

0 comments on commit 192e300

Please sign in to comment.