Merge remote-tracking branch 'refs/remotes/origin/main'
This commit is contained in:
+12
-2
@@ -7,6 +7,7 @@ default-run = "emulator"
|
||||
[lib]
|
||||
name = "dsa_rs"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[[bin]]
|
||||
name = "emulator"
|
||||
@@ -16,15 +17,24 @@ required-features = ["config"]
|
||||
common = { path = "../common" }
|
||||
assembler = { path = "../assembler" }
|
||||
dsa_editor = { path = "../dsa_editor" }
|
||||
eframe = "0.31.1"
|
||||
eframe = { version = "0.31.1" }
|
||||
egui = "0.31.1"
|
||||
rfd = "0.15.3"
|
||||
dirs = "6.0.0"
|
||||
discord-presence = { version = "1.6.0", optional = true }
|
||||
toml = { version = "0.8.23", optional = true }
|
||||
serde = { version = "1.0.219", features = ["derive"], optional = true }
|
||||
egui_file = "0.22.1"
|
||||
|
||||
[features]
|
||||
default = ["config"]
|
||||
discord-rpc = ["dep:discord-presence"]
|
||||
config = ["dep:toml", "dep:serde"]
|
||||
|
||||
# Add support for Android for the fun of it.
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
winit = { version = "0.30.11", features = ["android-native-activity"] }
|
||||
# jni = "0.21.1"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.eframe]
|
||||
version = "0.31.1"
|
||||
features = ["android-native-activity"]
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub mod rpc;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//!
|
||||
//! # Configuration
|
||||
//!
|
||||
//! This may be disabled like so in your `.dsarc.toml` file:
|
||||
//! This may be disabled like so in your `.dsa.emulator.toml` file:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [misc]
|
||||
@@ -16,24 +16,23 @@
|
||||
//!
|
||||
//! Alternatively, you can hide this in your Discord settings.
|
||||
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
Arc,
|
||||
mpsc::{Receiver, Sender},
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
use discord_presence::{Client, DiscordError, models::ActivityTimestamps};
|
||||
|
||||
use crate::emulator::config::Config;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub enum RpcClientError {
|
||||
Client(DiscordError),
|
||||
}
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
impl std::fmt::Display for RpcClientError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
@@ -42,8 +41,10 @@ impl std::fmt::Display for RpcClientError {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
impl std::error::Error for RpcClientError {}
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
impl From<DiscordError> for RpcClientError {
|
||||
fn from(err: DiscordError) -> Self {
|
||||
Self::Client(err)
|
||||
@@ -52,6 +53,7 @@ impl From<DiscordError> for RpcClientError {
|
||||
|
||||
/// The type of activity the user is currently doing.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub enum Activity {
|
||||
Idle,
|
||||
EditingFile(PathBuf),
|
||||
@@ -59,6 +61,7 @@ pub enum Activity {
|
||||
|
||||
/// Messages to send over the wire.
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub enum Message {
|
||||
/// Sent when we want to update the [`Context`].
|
||||
Update(Activity),
|
||||
@@ -66,9 +69,11 @@ pub enum Message {
|
||||
Stop,
|
||||
}
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
unsafe impl Send for Message {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
pub struct RpcClient {
|
||||
/// Sends updates to [`Context`] (our state).
|
||||
sender: Sender<Message>,
|
||||
@@ -76,6 +81,7 @@ pub struct RpcClient {
|
||||
thread_handle: Option<Arc<std::thread::JoinHandle<()>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
impl RpcClient {
|
||||
#[expect(clippy::unreadable_literal)]
|
||||
/// Sets up the [`RpcClient`].
|
||||
@@ -169,6 +175,7 @@ impl RpcClient {
|
||||
}
|
||||
|
||||
// Possibly unneeded but good practice.
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
impl Drop for RpcClient {
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
@@ -181,14 +188,31 @@ impl Drop for RpcClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Stub for when the feature is disabled.
|
||||
#[cfg(not(feature = "discord-rpc"))]
|
||||
pub struct RpcClient {}
|
||||
|
||||
/// Stub for when the feature is disabled.
|
||||
#[cfg(not(feature = "discord-rpc"))]
|
||||
pub enum Message {}
|
||||
|
||||
/// Stub for when the feature is disabled.
|
||||
#[cfg(not(feature = "discord-rpc"))]
|
||||
pub enum Activity {}
|
||||
|
||||
/// Gets the discord [`RpcClient`] or returns None if this has been disabled in the config
|
||||
/// options.
|
||||
#[cfg(feature = "config")]
|
||||
#[allow(clippy::needless_pass_by_value, unused_variables)]
|
||||
pub fn get_rpc_client_or_none(
|
||||
config: &Config,
|
||||
rpc_sender: Sender<Message>,
|
||||
rpc_reciever: Receiver<Message>,
|
||||
) -> Result<Option<RpcClient>, Box<dyn std::error::Error + 'static>> {
|
||||
#[cfg(not(feature = "discord-rpc"))]
|
||||
return Ok(None);
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
if config.misc.use_discord_rpc {
|
||||
Ok(Some(RpcClient::new(rpc_sender, rpc_reciever)?))
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
sync::mpsc::{self, Receiver, Sender},
|
||||
@@ -6,7 +5,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
#[allow(unused_imports)]
|
||||
use crate::emulator::misc::rpc::{Activity, RpcClient};
|
||||
|
||||
use crate::emulator::system::{
|
||||
@@ -17,11 +16,12 @@ use crate::emulator::system::{
|
||||
use common::prelude::*;
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
#[allow(unused_variables)]
|
||||
pub fn run_emulator(
|
||||
cmd_rx: &Receiver<Command>,
|
||||
state_tx: &Sender<State>,
|
||||
mut processor: Processor,
|
||||
#[cfg(feature = "discord-rpc")] rpc_client: Option<&Arc<RpcClient>>,
|
||||
rpc_client: Option<&Arc<RpcClient>>,
|
||||
) {
|
||||
println!("INFO: Starting emulator.");
|
||||
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
use std::{ffi::OsStr, path::PathBuf, sync::mpsc::Sender};
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
sync::mpsc::Sender,
|
||||
};
|
||||
|
||||
use common::prelude::Instruction;
|
||||
use egui::{Align, Context, Key, Layout, Ui};
|
||||
use rfd::FileDialog;
|
||||
|
||||
use dsa_editor::{CodeEditor, ColorTheme, Syntax};
|
||||
use egui_file::FileDialog;
|
||||
|
||||
use crate::emulator::{
|
||||
system::model::{Command, State},
|
||||
@@ -29,6 +33,10 @@ pub struct Editor {
|
||||
cursor_col: usize,
|
||||
cursor_line: usize,
|
||||
|
||||
// file dialogs
|
||||
open_file_dialog: Option<FileDialog>,
|
||||
save_file_dialog: Option<FileDialog>,
|
||||
|
||||
// other
|
||||
visible: bool,
|
||||
sender: Sender<Command>,
|
||||
@@ -98,6 +106,8 @@ impl Editor {
|
||||
load_offset: 0,
|
||||
offset_str: String::new(),
|
||||
error: None,
|
||||
open_file_dialog: None,
|
||||
save_file_dialog: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,36 +140,31 @@ impl Editor {
|
||||
}
|
||||
|
||||
fn save(&mut self) {
|
||||
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
|
||||
dirs::home_dir().expect(
|
||||
"Couldn't get your current working directory or your home directory.",
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(path) = &self.path {
|
||||
if let Err(why) = std::fs::write(path, &self.text) {
|
||||
self.error = Some(format!("Failed to save file: {why}"));
|
||||
return;
|
||||
}
|
||||
|
||||
self.buffer = self.text.clone();
|
||||
self.unsaved = false;
|
||||
return;
|
||||
if self.open_file_dialog.is_some() {
|
||||
// TODO: Flash an error stating you can only have one menu open at once.
|
||||
self.open_file_dialog = None;
|
||||
}
|
||||
|
||||
if let Some(path) = FileDialog::new()
|
||||
.add_filter("Assembly Files or Binaries", &["dsa", "dsb"])
|
||||
.add_filter("all", &["*"])
|
||||
.set_directory(&work_dir)
|
||||
.save_file()
|
||||
{
|
||||
if let Err(why) = std::fs::write(&path, &self.text) {
|
||||
if let Some(path) = &self.path {
|
||||
// Save to existing path
|
||||
if let Err(why) = std::fs::write(path, &self.text) {
|
||||
self.error = Some(format!("Failed to save file: {why}"));
|
||||
} else {
|
||||
self.path = Some(path);
|
||||
self.buffer = self.text.clone();
|
||||
self.unsaved = false;
|
||||
}
|
||||
} else {
|
||||
// Open the save dialog.
|
||||
let work_dir = std::env::current_dir().unwrap_or_else(|_| {
|
||||
dirs::home_dir().expect(
|
||||
"Couldn't get your current working directory or your home directory.",
|
||||
)
|
||||
});
|
||||
|
||||
if self.save_file_dialog.is_none() {
|
||||
let mut dialog = FileDialog::save_file(Some(work_dir));
|
||||
dialog.open();
|
||||
self.save_file_dialog = Some(dialog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,46 +175,125 @@ impl Editor {
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(path) = FileDialog::new()
|
||||
.add_filter("Assembly Files or Binaries", &["dsa", "dsb"])
|
||||
.add_filter("all", &["*"])
|
||||
.set_directory(&work_dir)
|
||||
.pick_file()
|
||||
{
|
||||
match path.extension().and_then(|ext| ext.to_str()) {
|
||||
Some("dsb") => {
|
||||
let contents = match std::fs::read(&path) {
|
||||
Ok(contents) => contents,
|
||||
Err(why) => {
|
||||
self.error = Some(format!("Failed to read file: {why}"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
// if let Some(path) = FileDialog::new()
|
||||
// .add_filter("Assembly Files or Binaries", &["dsa", "dsb"])
|
||||
// .add_filter("all", &["*"])
|
||||
// .set_directory(&work_dir)
|
||||
// .pick_file()
|
||||
// {
|
||||
// match path.extension().and_then(|ext| ext.to_str()) {
|
||||
// Some("dsb") => {
|
||||
// let contents = match std::fs::read(&path) {
|
||||
// Ok(contents) => contents,
|
||||
// Err(why) => {
|
||||
// self.error = Some(format!("Failed to read file: {why}"));
|
||||
// return;
|
||||
// }
|
||||
// };
|
||||
|
||||
self.path = Some(path.clone());
|
||||
self.output = contents;
|
||||
self.unsaved = false;
|
||||
self.text = String::from("Loaded Binary File!");
|
||||
self.buffer = self.text.clone();
|
||||
self.unsaved = false;
|
||||
}
|
||||
_ => {
|
||||
if let Ok(contents) = std::fs::read_to_string(&path) {
|
||||
self.path = Some(path.clone());
|
||||
self.text.clone_from(&contents);
|
||||
self.buffer = contents;
|
||||
self.unsaved = false;
|
||||
}
|
||||
}
|
||||
// self.path = Some(path.clone());
|
||||
// self.output = contents;
|
||||
// self.unsaved = false;
|
||||
// self.text = String::from("Loaded Binary File!");
|
||||
// self.buffer = self.text.clone();
|
||||
// self.unsaved = false;
|
||||
// }
|
||||
// _ => {
|
||||
// if let Ok(contents) = std::fs::read_to_string(&path) {
|
||||
// self.path = Some(path.clone());
|
||||
// self.text.clone_from(&contents);
|
||||
// self.buffer = contents;
|
||||
// self.unsaved = false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if self.save_file_dialog.is_some() {
|
||||
// TODO: Flash an error stating you can only have one menu open at once.
|
||||
self.save_file_dialog = None;
|
||||
}
|
||||
|
||||
if self.open_file_dialog.is_none() {
|
||||
if let Some(p) = &self.path {
|
||||
let path = p.parent().map(Path::to_path_buf);
|
||||
let mut dialog = FileDialog::open_file(path);
|
||||
dialog.open();
|
||||
self.open_file_dialog = Some(dialog);
|
||||
} else {
|
||||
let mut dialog = FileDialog::open_file(Some(work_dir));
|
||||
dialog.open();
|
||||
self.open_file_dialog = Some(dialog);
|
||||
}
|
||||
|
||||
std::env::set_current_dir(
|
||||
path.parent().expect("A file should be in a directory!"),
|
||||
)
|
||||
.expect("ERROR: Failed to set current working directory.");
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_file_dialogs(&mut self, ctx: &egui::Context) {
|
||||
// Handle open dialog
|
||||
if let Some(dialog) = &mut self.open_file_dialog {
|
||||
if dialog.show(ctx).selected() {
|
||||
if let Some(file) = dialog.path() {
|
||||
match std::fs::read_to_string(file) {
|
||||
Ok(content) => {
|
||||
self.text = content;
|
||||
self.path = Some(file.to_path_buf());
|
||||
self.unsaved = false;
|
||||
self.error = None;
|
||||
}
|
||||
Err(e) => {
|
||||
self.error = Some(format!("Failed to read file: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.open_file_dialog = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle save dialog
|
||||
if let Some(dialog) = &mut self.save_file_dialog {
|
||||
if dialog.show(ctx).selected() {
|
||||
if let Some(file) = dialog.path() {
|
||||
match std::fs::write(file, &self.text) {
|
||||
Ok(()) => {
|
||||
self.path = Some(file.to_path_buf());
|
||||
self.unsaved = false;
|
||||
self.error = None;
|
||||
}
|
||||
Err(e) => {
|
||||
self.error = Some(format!("Failed to save file: {e}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
self.save_file_dialog = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn open(&mut self) {
|
||||
// let work_dir = std::env::current_dir().unwrap_or_else(|_| {
|
||||
// dirs::home_dir().expect(
|
||||
// "Couldn't get your current working directory or your home directory.",
|
||||
// )
|
||||
// });
|
||||
|
||||
// if let Some(path) = FileDialog::new()
|
||||
// .add_filter("Assembly Files or Binaries", &["dsa", "dsb"])
|
||||
// .add_filter("all", &["*"])
|
||||
// .set_directory(&work_dir)
|
||||
// .pick_file()
|
||||
// {
|
||||
// if let Ok(contents) = std::fs::read_to_string(&path) {
|
||||
// self.path = Some(path.clone());
|
||||
// self.text.clone_from(&contents);
|
||||
// self.buffer = contents;
|
||||
// self.unsaved = false;
|
||||
// }
|
||||
|
||||
// std::env::set_current_dir(
|
||||
// path.parent().expect("A file should be in a directory!"),
|
||||
// )
|
||||
// .expect("ERROR: Failed to set current working directory.");
|
||||
// }
|
||||
// }
|
||||
|
||||
fn render_output(&self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||
// Output area with synchronized scrolling
|
||||
egui::ScrollArea::vertical()
|
||||
@@ -336,7 +420,9 @@ impl Editor {
|
||||
});
|
||||
}
|
||||
|
||||
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, ctx: &Context) {
|
||||
self.handle_file_dialogs(ctx);
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!("File type: {}", self.extension()));
|
||||
ui.label(format!("Filename: {}", self.filename()));
|
||||
|
||||
@@ -13,3 +13,117 @@
|
||||
)]
|
||||
|
||||
pub mod emulator;
|
||||
|
||||
use std::{
|
||||
sync::{
|
||||
Arc,
|
||||
mpsc::{Receiver, Sender},
|
||||
},
|
||||
thread,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
use winit::platform::android::{EventLoopBuilderExtAndroid, activity::AndroidApp};
|
||||
|
||||
use crate::emulator::{
|
||||
misc::rpc::RpcClient,
|
||||
system::{
|
||||
emulator::run_emulator,
|
||||
memory::MainStore,
|
||||
model::{Command, State},
|
||||
processor::Processor,
|
||||
},
|
||||
ui::{
|
||||
control_unit::ControlPanel, display::Display, editor::Editor,
|
||||
interface::EmulatorUI, memory_inspector::MemoryInspector,
|
||||
stack_inspector::StackInspector,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn android_main(app: AndroidApp) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use crate::emulator::{config::Config, misc::rpc::get_rpc_client_or_none};
|
||||
use std::path::Path;
|
||||
|
||||
// Initialize channels and read in configuration.
|
||||
let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel();
|
||||
let (state_sender, state_reciever) = std::sync::mpsc::channel();
|
||||
let config = Config::load(Path::new(".dsa.emulator.toml"))?;
|
||||
|
||||
// Setup RPC if enabled.
|
||||
let (rpc_sender, rpc_reciever) = std::sync::mpsc::channel();
|
||||
|
||||
let rpc_client =
|
||||
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
|
||||
|
||||
setup_emulator(cmd_receiver, state_sender, rpc_client);
|
||||
|
||||
let ui = setup_ui(cmd_sender, state_reciever);
|
||||
|
||||
// Run UI.
|
||||
#[allow(unused_variables)]
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
|
||||
event_loop_builder: Some(Box::new(move |builder| {
|
||||
#[cfg(target_os = "android")]
|
||||
builder.with_android_app(app);
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
eframe::run_native(
|
||||
"DSA Simulator (Damn Simple Architecture 🔥)",
|
||||
options,
|
||||
Box::new(move |cc| {
|
||||
cc.egui_ctx.set_visuals(egui::Visuals::default());
|
||||
Ok(Box::new(ui))
|
||||
}),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup_emulator(
|
||||
cmd_receiver: Receiver<Command>,
|
||||
state_sender: Sender<State>,
|
||||
rpc_client: Option<Arc<RpcClient>>,
|
||||
) {
|
||||
let main_store = MainStore::new();
|
||||
let processor = Processor::new(Box::new(main_store), vec![]);
|
||||
|
||||
thread::spawn(move || {
|
||||
run_emulator(&cmd_receiver, &state_sender, processor, rpc_client.as_ref());
|
||||
});
|
||||
}
|
||||
|
||||
/// Creates the [`EmulatorUI`].
|
||||
#[must_use]
|
||||
pub fn setup_ui(
|
||||
cmd_sender: Sender<Command>,
|
||||
state_reciever: Receiver<State>,
|
||||
) -> EmulatorUI {
|
||||
let mut ui = EmulatorUI::new(cmd_sender.clone(), state_reciever);
|
||||
|
||||
// Create UI modules.
|
||||
let control_unit = ControlPanel::new(cmd_sender.clone());
|
||||
|
||||
ui.add_component(Box::new(control_unit));
|
||||
|
||||
let mem_inspector = MemoryInspector::new(cmd_sender.clone());
|
||||
ui.add_component(Box::new(mem_inspector));
|
||||
|
||||
let stack_inspector = StackInspector::new();
|
||||
ui.add_component(Box::new(stack_inspector));
|
||||
|
||||
let editor = Editor::new(cmd_sender);
|
||||
ui.add_component(Box::new(editor));
|
||||
|
||||
let display = Display::new();
|
||||
ui.add_component(Box::new(display));
|
||||
|
||||
let history = emulator::ui::history::History::new();
|
||||
ui.add_component(Box::new(history));
|
||||
|
||||
ui
|
||||
}
|
||||
|
||||
+5
-74
@@ -1,28 +1,7 @@
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
path::Path,
|
||||
sync::mpsc::{Receiver, Sender},
|
||||
thread,
|
||||
};
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
use dsa_rs::emulator::misc::rpc::{RpcClient, get_rpc_client_or_none};
|
||||
|
||||
use dsa_rs::emulator::{
|
||||
config::Config,
|
||||
system::{
|
||||
emulator::run_emulator,
|
||||
memory::MainStore,
|
||||
model::{Command, State},
|
||||
processor::Processor,
|
||||
},
|
||||
ui::{
|
||||
control_unit::ControlPanel, display::Display, editor::Editor,
|
||||
interface::EmulatorUI, memory_inspector::MemoryInspector,
|
||||
stack_inspector::StackInspector,
|
||||
},
|
||||
};
|
||||
use dsa_rs::emulator::{config::Config, misc::rpc::get_rpc_client_or_none};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize channels and read in configuration.
|
||||
@@ -31,21 +10,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = Config::load(Path::new(".dsa.emulator.toml"))?;
|
||||
|
||||
// Setup RPC if enabled.
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
let (rpc_sender, rpc_reciever) = std::sync::mpsc::channel();
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
let rpc_client =
|
||||
get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new);
|
||||
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
setup_emulator(cmd_receiver, state_sender, rpc_client);
|
||||
#[cfg(not(feature = "discord-rpc"))]
|
||||
setup_emulator(cmd_receiver, state_sender);
|
||||
dsa_rs::setup_emulator(cmd_receiver, state_sender, rpc_client);
|
||||
|
||||
let ui = setup_ui(cmd_sender, state_reciever);
|
||||
let ui = dsa_rs::setup_ui(cmd_sender, state_reciever);
|
||||
|
||||
// Run UI.
|
||||
#[allow(unused_variables)]
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
|
||||
..Default::default()
|
||||
@@ -62,47 +37,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_emulator(
|
||||
cmd_receiver: Receiver<Command>,
|
||||
state_sender: Sender<State>,
|
||||
#[cfg(feature = "discord-rpc")] rpc_client: Option<Arc<RpcClient>>,
|
||||
) {
|
||||
let main_store = MainStore::new();
|
||||
let processor = Processor::new(Box::new(main_store), vec![]);
|
||||
|
||||
thread::spawn(move || {
|
||||
#[cfg(feature = "discord-rpc")]
|
||||
run_emulator(&cmd_receiver, &state_sender, processor, rpc_client.as_ref());
|
||||
|
||||
#[cfg(not(feature = "discord-rpc"))]
|
||||
run_emulator(&cmd_receiver, &state_sender, processor);
|
||||
});
|
||||
}
|
||||
|
||||
/// Creates the [`EmulatorUI`].
|
||||
fn setup_ui(cmd_sender: Sender<Command>, state_reciever: Receiver<State>) -> EmulatorUI {
|
||||
let mut ui = EmulatorUI::new(cmd_sender.clone(), state_reciever);
|
||||
|
||||
// Create UI modules.
|
||||
let control_unit = ControlPanel::new(cmd_sender.clone());
|
||||
|
||||
ui.add_component(Box::new(control_unit));
|
||||
|
||||
let mem_inspector = MemoryInspector::new(cmd_sender.clone());
|
||||
ui.add_component(Box::new(mem_inspector));
|
||||
|
||||
let stack_inspector = StackInspector::new();
|
||||
ui.add_component(Box::new(stack_inspector));
|
||||
|
||||
let editor = Editor::new(cmd_sender.clone());
|
||||
ui.add_component(Box::new(editor));
|
||||
|
||||
let display = Display::new();
|
||||
ui.add_component(Box::new(display));
|
||||
|
||||
let history = dsa_rs::emulator::ui::history::History::new();
|
||||
ui.add_component(Box::new(history));
|
||||
|
||||
ui
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user