added a simple process manager for the emulator UI. responsible for running and providing log information on build tasks etc.
first implementation so a bit jank still.
This commit is contained in:
@@ -15,6 +15,7 @@ use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use common::formats::binary_dse::{DseExecutable, SymbolEntry};
|
||||
use common::prelude::Instruction;
|
||||
use common::process::{ProcessAgent, TraceableProcess};
|
||||
use crate::assembler::resolver2::{resolve_address, resolve_dependencies, resolve_symbols, split_sections, SectionId};
|
||||
|
||||
// Module declarations
|
||||
@@ -31,8 +32,6 @@ pub mod parser;
|
||||
// pub mod resolver;
|
||||
pub mod resolver2;
|
||||
|
||||
|
||||
|
||||
// Re-exports
|
||||
pub use self::{
|
||||
codegen::codegen,
|
||||
@@ -43,6 +42,7 @@ pub use self::{
|
||||
// resolver::{create_sections, resolve_dependencies, resolve_symbols},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Assembler {
|
||||
src_path: PathBuf,
|
||||
result_tx: mpsc::Sender<Result<Vec<u8>, AssembleError>>,
|
||||
@@ -50,6 +50,15 @@ pub struct Assembler {
|
||||
is_running: bool,
|
||||
}
|
||||
|
||||
impl TraceableProcess for Assembler {
|
||||
type Output = DseExecutable;
|
||||
type Error = AssembleError;
|
||||
|
||||
fn run(mut self: Box<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error> {
|
||||
assemble(&self.src_path)
|
||||
}
|
||||
}
|
||||
|
||||
impl Assembler {
|
||||
#[must_use]
|
||||
pub fn new(src_path: impl Into<PathBuf>) -> Self {
|
||||
@@ -63,7 +72,7 @@ impl Assembler {
|
||||
}
|
||||
|
||||
/// Start the compilation process in a separate thread
|
||||
pub fn start(&mut self, args: ()) {
|
||||
pub fn start(&mut self) {
|
||||
if self.is_running {
|
||||
return;
|
||||
}
|
||||
@@ -143,8 +152,6 @@ impl Assembler {
|
||||
}
|
||||
}
|
||||
|
||||
impl Assembler {}
|
||||
|
||||
// fn assemble(src: &Path) -> Result<Vec<Instruction>, AssembleError> {
|
||||
// let mut modules = HashSet::new();
|
||||
// let mut program = Program::new();
|
||||
@@ -332,6 +339,8 @@ pub enum AssembleError {
|
||||
MissingArgument(u8),
|
||||
}
|
||||
|
||||
impl std::error::Error for AssembleError {}
|
||||
|
||||
impl fmt::Display for AssembleError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
||||
@@ -16,7 +16,7 @@ fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let mut engine = Assembler::new(PathBuf::from(args.input_path));
|
||||
engine.start(());
|
||||
engine.start();
|
||||
let result = engine.output().expect("assembler failed.");
|
||||
|
||||
if let Err(e) = fs::write(args.output_path, result) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod asm;
|
||||
pub mod isa;
|
||||
pub mod formats;
|
||||
pub mod process;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::isa::instructions::Instruction;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
use std::fmt::Debug;
|
||||
use std::sync::mpsc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProcessAgent {
|
||||
tx: mpsc::Sender<String>,
|
||||
}
|
||||
|
||||
impl ProcessAgent {
|
||||
pub fn new(tx: mpsc::Sender<String>) -> Self {
|
||||
Self { tx }
|
||||
}
|
||||
|
||||
pub fn log(&self, msg: impl Into<String>) {
|
||||
let _ = self.tx.send(msg.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TraceableProcess: Debug + Send + 'static {
|
||||
type Output: Send + 'static;
|
||||
type Error: std::error::Error + Send + 'static;
|
||||
fn run(self: Box<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error>;
|
||||
}
|
||||
@@ -3,4 +3,4 @@
|
||||
#![feature(str_as_str)]
|
||||
|
||||
pub mod ui;
|
||||
pub mod args;
|
||||
pub mod args;
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use eframe::NativeOptions;
|
||||
use egui::{CentralPanel, Panel, Ui};
|
||||
use egui_tiles::TileId;
|
||||
use common::formats::binary_dse::DseExecutable;
|
||||
use dialog::confirm_dialog::ConfirmDialog;
|
||||
use panels::documentation::Documentation;
|
||||
use panels::editor::Editor;
|
||||
use dialog::file_dialog::DsaFileDialog;
|
||||
use emulator_core::{EmulatorController};
|
||||
use panels::PanelFactory;
|
||||
use crate::ui::controller::Controller;
|
||||
use crate::ui::dialog::error_dialog::ErrorDialog;
|
||||
use crate::ui::panels::{CentralTiles, TilePane};
|
||||
use crate::ui::panels::about::AboutScreen;
|
||||
use crate::ui::panels::disassembler::BinaryViewer;
|
||||
use crate::ui::panels::display::Display;
|
||||
@@ -23,11 +7,30 @@ use crate::ui::panels::framebuffer::FrameBuffer;
|
||||
use crate::ui::panels::memory::MemoryInspector;
|
||||
use crate::ui::panels::registers::Registers;
|
||||
use crate::ui::panels::serial::Serial;
|
||||
use crate::ui::panels::{CentralTiles, TilePane};
|
||||
use common::formats::binary_dse::DseExecutable;
|
||||
use common::process::TraceableProcess;
|
||||
use dialog::confirm_dialog::ConfirmDialog;
|
||||
use dialog::file_dialog::DsaFileDialog;
|
||||
use eframe::NativeOptions;
|
||||
use egui::{CentralPanel, Panel, Ui};
|
||||
use egui_tiles::TileId;
|
||||
use emulator_core::EmulatorController;
|
||||
use panels::PanelFactory;
|
||||
use panels::documentation::Documentation;
|
||||
use panels::editor::Editor;
|
||||
use process_manager::{ProcessManager, ProcessSpawner};
|
||||
use std::fmt::Debug;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
mod dialog;
|
||||
mod panels;
|
||||
mod ui_helpers;
|
||||
mod controller;
|
||||
pub mod process_manager;
|
||||
|
||||
pub fn run_app(mut controller: EmulatorController) -> eframe::Result<()> {
|
||||
// initialise emulator
|
||||
@@ -56,8 +59,8 @@ pub enum DataSource {
|
||||
Data(Vec<u8>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Action {
|
||||
None,
|
||||
RequestCloseTab(TileId),
|
||||
CloseTab(TileId),
|
||||
Quit,
|
||||
@@ -67,6 +70,7 @@ pub enum Action {
|
||||
SaveFile(PathBuf, TileId),
|
||||
RequestSaveAs(TileId),
|
||||
ShowError(String),
|
||||
SpawnProcess(ProcessSpawner)
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +84,8 @@ pub struct DamnSimpleUI {
|
||||
error_dialog: ErrorDialog,
|
||||
|
||||
tools: Vec<PanelFactory>,
|
||||
|
||||
process_manager: ProcessManager
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +101,10 @@ impl eframe::App for DamnSimpleUI {
|
||||
self.perform_action(action);
|
||||
}
|
||||
|
||||
while let Some(action) = self.process_manager.get_action() {
|
||||
self.perform_action(action);
|
||||
}
|
||||
|
||||
self.error_dialog.show(ui);
|
||||
|
||||
Panel::top("menu_bar").resizable(false).show(ui, |ui| {
|
||||
@@ -109,8 +119,8 @@ impl eframe::App for DamnSimpleUI {
|
||||
// TODO: File Explorer!
|
||||
});
|
||||
|
||||
Panel::bottom("bottom_panel").resizable(false).show(ui, |ui| {
|
||||
// self.process_manager.ui(ui);
|
||||
Panel::bottom("bottom_panel").resizable(true).show(ui, |ui| {
|
||||
self.process_manager.ui(ui);
|
||||
});
|
||||
|
||||
CentralPanel::default().show(ui, |ui| {
|
||||
@@ -174,12 +184,15 @@ impl DamnSimpleUI {
|
||||
})
|
||||
],
|
||||
emulator,
|
||||
process_manager: ProcessManager::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn perform_action(&mut self, action: Action) {
|
||||
match action {
|
||||
Action::None => {},
|
||||
|
||||
Action::SpawnProcess(spawner) => spawner(&mut self.process_manager),
|
||||
Action::RequestCloseTab(tile_id) => {
|
||||
self.confirm_dialog.open(
|
||||
"Close Tab",
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
use crate::ui::Action;
|
||||
use crate::ui::panels::TilePane;
|
||||
use common::process::{ProcessAgent, TraceableProcess};
|
||||
use egui::{Key, Ui};
|
||||
use egui_code_editor::CodeEditor;
|
||||
use egui_tiles::TileId;
|
||||
use std::any::Any;
|
||||
use std::io::Error;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use assembler::prelude::Assembler;
|
||||
use theme::THEME;
|
||||
use crate::ui::DataSource::Data;
|
||||
|
||||
mod syntax;
|
||||
mod theme;
|
||||
|
||||
@@ -56,27 +63,54 @@ impl TilePane for Editor {
|
||||
|
||||
fn ui(&mut self, ui: &mut Ui, tile_id: TileId) -> Option<Action> {
|
||||
|
||||
egui::Panel::top("panel").show(ui, |ui| {
|
||||
let response = egui::Panel::top("panel").show(ui, |ui| {
|
||||
let mut action = None;
|
||||
|
||||
if let Some(filename) = self.filename.as_ref() {
|
||||
ui.label(filename.display().to_string());
|
||||
} else {
|
||||
ui.label("New File*");
|
||||
}
|
||||
|
||||
if ui.button("Build").clicked() {
|
||||
|
||||
let Some(filename) = self.filename.clone() else {
|
||||
println!("error!");
|
||||
return None;
|
||||
};
|
||||
|
||||
action = Some(Action::SpawnProcess(Box::new(move |pm| {
|
||||
pm.spawn(
|
||||
Assembler::new(filename),
|
||||
move |result| {
|
||||
return if let Ok(bin) = result {
|
||||
Action::LoadBinary(Data(bin.to_memory_image()))
|
||||
} else {
|
||||
Action::None
|
||||
}
|
||||
}
|
||||
);
|
||||
})));
|
||||
};
|
||||
|
||||
action
|
||||
});
|
||||
|
||||
let mut action = None;
|
||||
if let Some(action) = response.inner {
|
||||
return Some(action);
|
||||
}
|
||||
|
||||
self.editor.show(ui, &mut self.buffer, &syntax::dsa());
|
||||
|
||||
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
|
||||
if let Some(filename) = self.filename.clone() {
|
||||
action = Some(Action::SaveFile(filename, tile_id));
|
||||
return if let Some(filename) = self.filename.clone() {
|
||||
Some(Action::SaveFile(filename, tile_id))
|
||||
} else {
|
||||
action = Some(Action::RequestSaveAs(tile_id));
|
||||
Some(Action::RequestSaveAs(tile_id))
|
||||
}
|
||||
};
|
||||
|
||||
action
|
||||
None
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
@@ -86,3 +120,20 @@ impl TilePane for Editor {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
use crate::ui::Action;
|
||||
use common::process::{ProcessAgent, TraceableProcess};
|
||||
use egui::Ui;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::mpsc;
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
pub type ProcessSpawner = Box<dyn FnOnce(&mut ProcessManager) + Send>;
|
||||
|
||||
pub struct ProcessHandle {
|
||||
id: usize,
|
||||
name: String,
|
||||
rx: mpsc::Receiver<String>,
|
||||
join_handle: Option<JoinHandle<Action>>,
|
||||
|
||||
// current process state
|
||||
logs: Vec<String>,
|
||||
}
|
||||
|
||||
impl ProcessHandle {
|
||||
pub fn spawn<P: TraceableProcess>(
|
||||
id: usize,
|
||||
name: impl Into<String>,
|
||||
process: P,
|
||||
on_complete: impl FnOnce(Result<P::Output, P::Error>) -> Action + Send + 'static
|
||||
) -> Self {
|
||||
let (tx, rx) = mpsc::channel::<String>();
|
||||
let agent = ProcessAgent::new( tx );
|
||||
|
||||
let join_handle = std::thread::spawn(move || {
|
||||
let result = TraceableProcess::run(Box::new(process), agent);
|
||||
on_complete(result)
|
||||
});
|
||||
|
||||
Self {
|
||||
rx, id, name: name.into(), join_handle: Some(join_handle), logs: Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ProcessManager {
|
||||
_id_counter: usize,
|
||||
|
||||
handles: Vec<ProcessHandle>,
|
||||
terminated_actions: VecDeque<Action>,
|
||||
}
|
||||
|
||||
impl ProcessManager {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn new_id(&mut self) -> usize {
|
||||
let id = self._id_counter;
|
||||
self._id_counter += 1;
|
||||
id
|
||||
}
|
||||
|
||||
pub fn get_action(&mut self) -> Option<Action> {
|
||||
self.terminated_actions.pop_front()
|
||||
}
|
||||
|
||||
pub fn spawn<P: TraceableProcess>(&mut self, process: P, on_complete: impl FnOnce(Result<P::Output, P::Error>) -> Action + Send + 'static) {
|
||||
let handle = ProcessHandle::spawn(self.new_id(), "process", process, on_complete);
|
||||
self.handles.push(handle);
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, ui: &mut Ui) {
|
||||
for handle in &mut self.handles {
|
||||
let is_finished = handle.join_handle
|
||||
.as_ref()
|
||||
.is_some_and(|jh| jh.is_finished());
|
||||
|
||||
if is_finished {
|
||||
if let Some(jh) = handle.join_handle.take() {
|
||||
println!("finished! {}", handle.name);
|
||||
if let Ok(action) = jh.join() {
|
||||
self.terminated_actions.push_back(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
for h in &self.handles {
|
||||
ui.label(&h.name);
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user