From 22a8785083917fa27613d238721009dcfc0f5abc Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 22 Jun 2025 00:03:48 +0100 Subject: [PATCH 1/7] emulator: cut down on cfg directives --- emulator/src/emulator/misc/mod.rs | 1 - emulator/src/emulator/misc/rpc.rs | 27 ++++++++++++++++++++++++ emulator/src/emulator/system/emulator.rs | 5 +---- emulator/src/main.rs | 13 +----------- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/emulator/src/emulator/misc/mod.rs b/emulator/src/emulator/misc/mod.rs index 7689ddb..06a3fd0 100644 --- a/emulator/src/emulator/misc/mod.rs +++ b/emulator/src/emulator/misc/mod.rs @@ -1,2 +1 @@ -#[cfg(feature = "discord-rpc")] pub mod rpc; diff --git a/emulator/src/emulator/misc/rpc.rs b/emulator/src/emulator/misc/rpc.rs index c548874..183d773 100644 --- a/emulator/src/emulator/misc/rpc.rs +++ b/emulator/src/emulator/misc/rpc.rs @@ -25,15 +25,18 @@ use std::{ time::Duration, }; +#[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 +45,10 @@ impl std::fmt::Display for RpcClientError { } } +#[cfg(feature = "discord-rpc")] impl std::error::Error for RpcClientError {} +#[cfg(feature = "discord-rpc")] impl From for RpcClientError { fn from(err: DiscordError) -> Self { Self::Client(err) @@ -52,6 +57,7 @@ impl From 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 +65,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 +73,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, @@ -76,6 +85,7 @@ pub struct RpcClient { thread_handle: Option>>, } +#[cfg(feature = "discord-rpc")] impl RpcClient { #[expect(clippy::unreadable_literal)] /// Sets up the [`RpcClient`]. @@ -169,6 +179,7 @@ impl RpcClient { } // Possibly unneeded but good practice. +#[cfg(feature = "discord-rpc")] impl Drop for RpcClient { fn drop(&mut self) { self.stop(); @@ -181,6 +192,18 @@ 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")] @@ -189,6 +212,10 @@ pub fn get_rpc_client_or_none( rpc_sender: Sender, rpc_reciever: Receiver, ) -> Result, Box> { + #[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 { diff --git a/emulator/src/emulator/system/emulator.rs b/emulator/src/emulator/system/emulator.rs index 6619b8d..46638d2 100644 --- a/emulator/src/emulator/system/emulator.rs +++ b/emulator/src/emulator/system/emulator.rs @@ -1,4 +1,3 @@ -#[cfg(feature = "discord-rpc")] use std::sync::Arc; use std::{ sync::mpsc::{self, Receiver, Sender}, @@ -6,9 +5,7 @@ use std::{ time::Duration, }; -#[cfg(feature = "discord-rpc")] use crate::emulator::misc::rpc::{Activity, RpcClient}; - use crate::emulator::system::{ model::{Command, PersistentState, Running, State}, processor::Processor, @@ -21,7 +18,7 @@ pub fn run_emulator( cmd_rx: &Receiver, state_tx: &Sender, mut processor: Processor, - #[cfg(feature = "discord-rpc")] rpc_client: Option<&Arc>, + rpc_client: Option<&Arc>, ) { println!("INFO: Starting emulator."); diff --git a/emulator/src/main.rs b/emulator/src/main.rs index fef27a5..b51bf1f 100644 --- a/emulator/src/main.rs +++ b/emulator/src/main.rs @@ -1,4 +1,3 @@ -#[cfg(feature = "discord-rpc")] use std::sync::Arc; use std::{ path::Path, @@ -6,7 +5,6 @@ use std::{ thread, }; -#[cfg(feature = "discord-rpc")] use dsa_rs::emulator::misc::rpc::{RpcClient, get_rpc_client_or_none}; use dsa_rs::emulator::{ @@ -31,17 +29,12 @@ fn main() -> Result<(), Box> { 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); let ui = setup_ui(cmd_sender, state_reciever); @@ -66,17 +59,13 @@ fn main() -> Result<(), Box> { fn setup_emulator( cmd_receiver: Receiver, state_sender: Sender, - #[cfg(feature = "discord-rpc")] rpc_client: Option>, + rpc_client: Option>, ) { 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); }); } From 1907bbb20018922ffd469bfbc784d21919496a63 Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 22 Jun 2025 00:30:27 +0100 Subject: [PATCH 2/7] misc: clippy fixes --- Cargo.lock | 1 + assembler/src/assembler/parser.rs | 16 ++++++++-------- assembler/src/main.rs | 8 ++++---- assembler/src/tooling/project.rs | 2 +- assembler/src/util/mod.rs | 2 +- emulator/Cargo.toml | 2 ++ emulator/src/emulator/misc/rpc.rs | 9 +-------- emulator/src/emulator/system/emulator.rs | 2 +- emulator/src/emulator/ui/editor.rs | 5 ++--- 9 files changed, 21 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8df113..60f846a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1064,6 +1064,7 @@ dependencies = [ "rfd", "serde", "toml", + "winit", ] [[package]] diff --git a/assembler/src/assembler/parser.rs b/assembler/src/assembler/parser.rs index 73c23fa..9f8734d 100644 --- a/assembler/src/assembler/parser.rs +++ b/assembler/src/assembler/parser.rs @@ -108,10 +108,10 @@ impl Parser { let dest = expect_type!(self.next()?, Register)?; let mut offset = Token::Immediate(0); - if let Ok(next) = self.peek_next() { - if expect_type!(next, Immediate).is_ok() { - offset = self.next()?; - } + if let Ok(next) = self.peek_next() + && expect_type!(next, Immediate).is_ok() + { + offset = self.next()?; } args = vec![base, dest, offset]; @@ -120,10 +120,10 @@ impl Parser { let base = expect_type!(self.next()?, Register)?; let dest = expect_type!(self.next()?, Register, Symbol)?; let mut offset = Token::Immediate(0); - if let Ok(next) = self.peek_next() { - if expect_type!(next, Immediate).is_ok() { - offset = self.next()?; - } + if let Ok(next) = self.peek_next() + && expect_type!(next, Immediate).is_ok() + { + offset = self.next()?; } args = vec![base, dest, offset]; } diff --git a/assembler/src/main.rs b/assembler/src/main.rs index 0d4f523..d1f61cc 100644 --- a/assembler/src/main.rs +++ b/assembler/src/main.rs @@ -23,7 +23,7 @@ fn main() { let mut output_file = match fs::File::create(output_path) { Ok(file) => file, Err(e) => { - eprintln!("Failed to create output file: {}", e); + eprintln!("Failed to create output file: {e}"); std::process::exit(1); } }; @@ -33,7 +33,7 @@ fn main() { // Assemble the source file if let Err(e) = engine.assemble(&src) { - eprintln!("Assembly error: {}", e); + eprintln!("Assembly error: {e}"); std::process::exit(1); } @@ -43,13 +43,13 @@ fn main() { for instruction in instructions { if let Err(e) = output_file.write_all(&instruction.encode().to_le_bytes()) { - eprintln!("Failed to write to output file: {}", e); + eprintln!("Failed to write to output file: {e}"); std::process::exit(1); } } } Some(Err(e)) => { - eprintln!("Build error: {}", e); + eprintln!("Build error: {e}"); std::process::exit(1); } None => { diff --git a/assembler/src/tooling/project.rs b/assembler/src/tooling/project.rs index 9195872..bdab388 100644 --- a/assembler/src/tooling/project.rs +++ b/assembler/src/tooling/project.rs @@ -20,7 +20,7 @@ pub fn tool_libcreate() { _ => panic!("Invalid project type"), }; - let path = format!("{}/{}.dsa", project_path, project_name); + let path = format!("{project_path}/{project_name}.dsa"); std::fs::write(path, template).expect("Unable to write file"); } diff --git a/assembler/src/util/mod.rs b/assembler/src/util/mod.rs index ea56139..01f628d 100644 --- a/assembler/src/util/mod.rs +++ b/assembler/src/util/mod.rs @@ -3,7 +3,7 @@ pub mod logging; use std::io::Write; pub fn input(prompt: &str) -> String { - print!("{}\n > ", prompt); + print!("{prompt}\n > "); std::io::stdout().flush().unwrap(); let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap(); diff --git a/emulator/Cargo.toml b/emulator/Cargo.toml index b72f082..722882e 100644 --- a/emulator/Cargo.toml +++ b/emulator/Cargo.toml @@ -7,6 +7,7 @@ default-run = "emulator" [lib] name = "dsa_rs" path = "src/lib.rs" +type = "cdylib" [[bin]] name = "emulator" @@ -23,6 +24,7 @@ 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 } +winit = { version = "0.30.11", features = ["android-native-activity"] } [features] default = ["config"] diff --git a/emulator/src/emulator/misc/rpc.rs b/emulator/src/emulator/misc/rpc.rs index 183d773..086750d 100644 --- a/emulator/src/emulator/misc/rpc.rs +++ b/emulator/src/emulator/misc/rpc.rs @@ -16,14 +16,7 @@ //! //! Alternatively, you can hide this in your Discord settings. -use std::{ - path::PathBuf, - sync::{ - Arc, - mpsc::{Receiver, Sender}, - }, - time::Duration, -}; +use std::sync::mpsc::{Receiver, Sender}; #[cfg(feature = "discord-rpc")] use discord_presence::{Client, DiscordError, models::ActivityTimestamps}; diff --git a/emulator/src/emulator/system/emulator.rs b/emulator/src/emulator/system/emulator.rs index 46638d2..342fcc4 100644 --- a/emulator/src/emulator/system/emulator.rs +++ b/emulator/src/emulator/system/emulator.rs @@ -5,7 +5,7 @@ use std::{ time::Duration, }; -use crate::emulator::misc::rpc::{Activity, RpcClient}; +use crate::emulator::misc::rpc::RpcClient; use crate::emulator::system::{ model::{Command, PersistentState, Running, State}, processor::Processor, diff --git a/emulator/src/emulator/ui/editor.rs b/emulator/src/emulator/ui/editor.rs index 98cc1c2..8c1e1de 100644 --- a/emulator/src/emulator/ui/editor.rs +++ b/emulator/src/emulator/ui/editor.rs @@ -344,8 +344,8 @@ impl Editor { } // builds the current file - if ui.button("Build").clicked() && !self.unsaved { - if let Some(path) = &self.path { + if ui.button("Build").clicked() && !self.unsaved + && let Some(path) = &self.path { let mut assembler = CompilerEngine::new(); if let Err(error) = assembler.assemble(path) { self.error = Some(format!("Failed to assemble: {error:?}")); @@ -371,7 +371,6 @@ impl Editor { .flat_map(|i| i.encode().to_be_bytes().to_vec()) .collect(); } - } // Loads the generated binary into the assembler at the provided offset if ui.button("Load").clicked() { From bbf893290f40cdf8b5495470f70a2050af28c1a0 Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 22 Jun 2025 00:42:44 +0100 Subject: [PATCH 3/7] misc: more clippy fixes, **switched to stable** The switch was due to rust-analyzer bug on latest nightly, please use stable until [this bug](https://github.com/rust-lang/rust-analyzer/issues/20051) is fixed --- assembler/src/assembler/parser.rs | 16 ++++++++-------- emulator/src/emulator/misc/rpc.rs | 6 +++++- emulator/src/emulator/system/emulator.rs | 5 ++++- emulator/src/emulator/ui/editor.rs | 5 +++-- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/assembler/src/assembler/parser.rs b/assembler/src/assembler/parser.rs index 9f8734d..73c23fa 100644 --- a/assembler/src/assembler/parser.rs +++ b/assembler/src/assembler/parser.rs @@ -108,10 +108,10 @@ impl Parser { let dest = expect_type!(self.next()?, Register)?; let mut offset = Token::Immediate(0); - if let Ok(next) = self.peek_next() - && expect_type!(next, Immediate).is_ok() - { - offset = self.next()?; + if let Ok(next) = self.peek_next() { + if expect_type!(next, Immediate).is_ok() { + offset = self.next()?; + } } args = vec![base, dest, offset]; @@ -120,10 +120,10 @@ impl Parser { let base = expect_type!(self.next()?, Register)?; let dest = expect_type!(self.next()?, Register, Symbol)?; let mut offset = Token::Immediate(0); - if let Ok(next) = self.peek_next() - && expect_type!(next, Immediate).is_ok() - { - offset = self.next()?; + if let Ok(next) = self.peek_next() { + if expect_type!(next, Immediate).is_ok() { + offset = self.next()?; + } } args = vec![base, dest, offset]; } diff --git a/emulator/src/emulator/misc/rpc.rs b/emulator/src/emulator/misc/rpc.rs index 086750d..3ef47e3 100644 --- a/emulator/src/emulator/misc/rpc.rs +++ b/emulator/src/emulator/misc/rpc.rs @@ -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,6 +16,9 @@ //! //! Alternatively, you can hide this in your Discord settings. +#[cfg(feature = "discord-rpc")] +use std::{path::PathBuf, sync::Arc, time::Duration}; + use std::sync::mpsc::{Receiver, Sender}; #[cfg(feature = "discord-rpc")] @@ -200,6 +203,7 @@ 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, diff --git a/emulator/src/emulator/system/emulator.rs b/emulator/src/emulator/system/emulator.rs index 342fcc4..24daa34 100644 --- a/emulator/src/emulator/system/emulator.rs +++ b/emulator/src/emulator/system/emulator.rs @@ -5,7 +5,9 @@ use std::{ time::Duration, }; -use crate::emulator::misc::rpc::RpcClient; +#[allow(unused_imports)] +use crate::emulator::misc::rpc::{Activity, RpcClient}; + use crate::emulator::system::{ model::{Command, PersistentState, Running, State}, processor::Processor, @@ -14,6 +16,7 @@ use crate::emulator::system::{ use common::prelude::*; #[expect(clippy::too_many_lines)] +#[allow(unused_variables)] pub fn run_emulator( cmd_rx: &Receiver, state_tx: &Sender, diff --git a/emulator/src/emulator/ui/editor.rs b/emulator/src/emulator/ui/editor.rs index 8c1e1de..98cc1c2 100644 --- a/emulator/src/emulator/ui/editor.rs +++ b/emulator/src/emulator/ui/editor.rs @@ -344,8 +344,8 @@ impl Editor { } // builds the current file - if ui.button("Build").clicked() && !self.unsaved - && let Some(path) = &self.path { + if ui.button("Build").clicked() && !self.unsaved { + if let Some(path) = &self.path { let mut assembler = CompilerEngine::new(); if let Err(error) = assembler.assemble(path) { self.error = Some(format!("Failed to assemble: {error:?}")); @@ -371,6 +371,7 @@ impl Editor { .flat_map(|i| i.encode().to_be_bytes().to_vec()) .collect(); } + } // Loads the generated binary into the assembler at the provided offset if ui.button("Load").clicked() { From 83259b92175921e484b151bdea77ffbc9d1e42c3 Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 22 Jun 2025 02:12:26 +0100 Subject: [PATCH 4/7] emulator: use egui file pickers rather than native ones TODO: Add file types when picking? This is a regression --- emulator/Cargo.toml | 13 ++- emulator/src/emulator/ui/editor.rs | 160 +++++++++++++++++++++-------- 2 files changed, 128 insertions(+), 45 deletions(-) diff --git a/emulator/Cargo.toml b/emulator/Cargo.toml index 722882e..f4cf832 100644 --- a/emulator/Cargo.toml +++ b/emulator/Cargo.toml @@ -17,14 +17,23 @@ 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" + +# 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"] [features] default = ["config"] diff --git a/emulator/src/emulator/ui/editor.rs b/emulator/src/emulator/ui/editor.rs index 98cc1c2..45a74bd 100644 --- a/emulator/src/emulator/ui/editor.rs +++ b/emulator/src/emulator/ui/editor.rs @@ -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, + save_file_dialog: Option, + // other visible: bool, sender: Sender, @@ -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,26 +175,93 @@ 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() - { - 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; + } - std::env::set_current_dir( - path.parent().expect("A file should be in a directory!"), - ) - .expect("ERROR: Failed to set current working directory."); + 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); + } } } + 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() @@ -316,7 +388,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())); From b97dcd569274117616b614a843eefa921b59946e Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 22 Jun 2025 02:13:06 +0100 Subject: [PATCH 5/7] emulator: start supporting Android, this is WIP Needs storage permissions, probably extra tweaks --- .gitignore | 1 + .vscode/settings.json | 8 +- Cargo.lock | 344 ++++++----------------------------- emulator/AndroidManifest.xml | 24 +++ emulator/src/main.rs | 18 +- 5 files changed, 107 insertions(+), 288 deletions(-) create mode 100644 emulator/AndroidManifest.xml diff --git a/.gitignore b/.gitignore index ea8c4bf..a851572 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +**/*.env \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 7af2f3a..0d3ac28 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,9 @@ { "rust-analyzer.check.command": "clippy", "editor.formatOnSave": true, - "rust-analyzer.cargo.features": "all" -} \ No newline at end of file + "rust-analyzer.cargo.features": "all", + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true +} diff --git a/Cargo.lock b/Cargo.lock index 60f846a..86a5b9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,7 @@ dependencies = [ "atspi-common", "serde", "thiserror 1.0.69", - "zvariant 4.2.0", + "zvariant", ] [[package]] @@ -78,7 +78,7 @@ dependencies = [ "futures-lite", "futures-util", "serde", - "zbus 4.4.0", + "zbus", ] [[package]] @@ -212,25 +212,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "ashpd" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" -dependencies = [ - "async-fs", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.9.1", - "raw-window-handle", - "serde", - "serde_repr", - "url", - "zbus 5.7.1", -] - [[package]] name = "assembler" version = "0.2.0" @@ -319,17 +300,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - [[package]] name = "async-process" version = "2.3.1" @@ -421,11 +391,11 @@ dependencies = [ "enumflags2", "serde", "static_assertions", - "zbus 4.4.0", + "zbus", "zbus-lockstep", "zbus-lockstep-macros", - "zbus_names 3.0.0", - "zvariant 4.2.0", + "zbus_names", + "zvariant", ] [[package]] @@ -437,7 +407,7 @@ dependencies = [ "atspi-common", "atspi-proxies", "futures-lite", - "zbus 4.4.0", + "zbus", ] [[package]] @@ -448,8 +418,8 @@ checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" dependencies = [ "atspi-common", "serde", - "zbus 4.4.0", - "zvariant 4.2.0", + "zbus", + "zvariant", ] [[package]] @@ -512,15 +482,6 @@ dependencies = [ "objc2 0.5.2", ] -[[package]] -name = "block2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" -dependencies = [ - "objc2 0.6.1", -] - [[package]] name = "blocking" version = "1.6.1" @@ -848,18 +809,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "dispatch2" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" -dependencies = [ - "bitflags 2.9.1", - "block2 0.6.1", - "libc", - "objc2 0.6.1", -] - [[package]] name = "dispatch2" version = "0.3.0" @@ -921,6 +870,12 @@ dependencies = [ "serde", ] +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + [[package]] name = "ecolor" version = "0.31.1" @@ -1023,6 +978,16 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_file" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e7289fecaa1af3f4944a7ac6e1c187d0700e32716c2a4c76d6bad7ffd255d72" +dependencies = [ + "dyn-clone", + "egui", +] + [[package]] name = "egui_glow" version = "0.31.1" @@ -1061,7 +1026,7 @@ dependencies = [ "dsa_editor", "eframe", "egui", - "rfd", + "egui_file", "serde", "toml", "winit", @@ -1228,15 +1193,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", -] - [[package]] name = "futures-core" version = "0.3.31" @@ -1377,7 +1333,7 @@ dependencies = [ "bitflags 2.9.1", "cfg_aliases", "cgl", - "dispatch2 0.3.0", + "dispatch2", "glutin_egl_sys", "glutin_glx_sys", "glutin_wgl_sys", @@ -1915,19 +1871,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "cfg_aliases", - "libc", - "memoffset", -] - [[package]] name = "nohash-hasher" version = "0.2.0" @@ -2026,7 +1969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.9.1", - "block2 0.5.1", + "block2", "libc", "objc2 0.5.2", "objc2-core-data", @@ -2042,7 +1985,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ "bitflags 2.9.1", - "block2 0.6.1", "objc2 0.6.1", "objc2-core-foundation", "objc2-core-graphics", @@ -2056,7 +1998,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.9.1", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -2068,7 +2010,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2080,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.9.1", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2092,7 +2034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags 2.9.1", - "dispatch2 0.3.0", + "dispatch2", "objc2 0.6.1", ] @@ -2103,7 +2045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" dependencies = [ "bitflags 2.9.1", - "dispatch2 0.3.0", + "dispatch2", "objc2 0.6.1", "objc2-core-foundation", "objc2-io-surface", @@ -2115,7 +2057,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", @@ -2127,7 +2069,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-contacts", "objc2-foundation 0.2.2", @@ -2146,7 +2088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.9.1", - "block2 0.5.1", + "block2", "dispatch", "libc", "objc2 0.5.2", @@ -2180,7 +2122,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -2193,7 +2135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.9.1", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2205,7 +2147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.9.1", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", "objc2-metal", @@ -2228,7 +2170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.9.1", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", @@ -2248,7 +2190,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-foundation 0.2.2", ] @@ -2260,7 +2202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.9.1", - "block2 0.5.1", + "block2", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", @@ -2433,12 +2375,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "pollster" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" - [[package]] name = "potential_utf" version = "0.1.2" @@ -2529,7 +2465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bd9640e0addc098a3481fd53bdc23970e5dd0edf6b349403aa680fb576c8f83" dependencies = [ "cfg-if", - "nix 0.29.0", + "nix", "quork-proc", "thiserror 2.0.12", "windows-sys 0.59.0", @@ -2570,18 +2506,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_chacha", + "rand_core", ] [[package]] @@ -2591,17 +2517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", + "rand_core", ] [[package]] @@ -2613,15 +2529,6 @@ dependencies = [ "getrandom 0.2.16", ] -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.3", -] - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -2663,30 +2570,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" -[[package]] -name = "rfd" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" -dependencies = [ - "ashpd", - "block2 0.6.1", - "dispatch2 0.2.0", - "js-sys", - "log", - "objc2 0.6.1", - "objc2-app-kit 0.3.1", - "objc2-core-foundation", - "objc2-foundation 0.3.1", - "pollster", - "raw-window-handle", - "urlencoding", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.59.0", -] - [[package]] name = "rustc-hash" version = "1.1.0" @@ -3246,15 +3129,8 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -4011,7 +3887,7 @@ dependencies = [ "android-activity", "atomic-waker", "bitflags 2.9.1", - "block2 0.5.1", + "block2", "bytemuck", "calloop", "cfg_aliases", @@ -4196,9 +4072,9 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix 0.29.0", + "nix", "ordered-stream", - "rand 0.8.5", + "rand", "serde", "serde_repr", "sha1", @@ -4207,42 +4083,9 @@ dependencies = [ "uds_windows", "windows-sys 0.52.0", "xdg-home", - "zbus_macros 4.4.0", - "zbus_names 3.0.0", - "zvariant 4.2.0", -] - -[[package]] -name = "zbus" -version = "5.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" -dependencies = [ - "async-broadcast", - "async-executor", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "nix 0.30.1", - "ordered-stream", - "serde", - "serde_repr", - "tracing", - "uds_windows", - "windows-sys 0.59.0", - "winnow", - "zbus_macros 5.7.1", - "zbus_names 4.2.0", - "zvariant 5.5.3", + "zbus_macros", + "zbus_names", + "zvariant", ] [[package]] @@ -4252,7 +4095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" dependencies = [ "zbus_xml", - "zvariant 4.2.0", + "zvariant", ] [[package]] @@ -4266,7 +4109,7 @@ dependencies = [ "syn", "zbus-lockstep", "zbus_xml", - "zvariant 4.2.0", + "zvariant", ] [[package]] @@ -4279,22 +4122,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "zvariant_utils 2.1.0", -] - -[[package]] -name = "zbus_macros" -version = "5.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zbus_names 4.2.0", - "zvariant 5.5.3", - "zvariant_utils 3.2.0", + "zvariant_utils", ] [[package]] @@ -4305,19 +4133,7 @@ checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", - "zvariant 4.2.0", -] - -[[package]] -name = "zbus_names" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97" -dependencies = [ - "serde", - "static_assertions", - "winnow", - "zvariant 5.5.3", + "zvariant", ] [[package]] @@ -4329,8 +4145,8 @@ dependencies = [ "quick-xml 0.30.0", "serde", "static_assertions", - "zbus_names 3.0.0", - "zvariant 4.2.0", + "zbus_names", + "zvariant", ] [[package]] @@ -4417,22 +4233,7 @@ dependencies = [ "enumflags2", "serde", "static_assertions", - "zvariant_derive 4.2.0", -] - -[[package]] -name = "zvariant" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" -dependencies = [ - "endi", - "enumflags2", - "serde", - "url", - "winnow", - "zvariant_derive 5.5.3", - "zvariant_utils 3.2.0", + "zvariant_derive", ] [[package]] @@ -4445,20 +4246,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "zvariant_utils 2.1.0", -] - -[[package]] -name = "zvariant_derive" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zvariant_utils 3.2.0", + "zvariant_utils", ] [[package]] @@ -4471,17 +4259,3 @@ dependencies = [ "quote", "syn", ] - -[[package]] -name = "zvariant_utils" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "static_assertions", - "syn", - "winnow", -] diff --git a/emulator/AndroidManifest.xml b/emulator/AndroidManifest.xml new file mode 100644 index 0000000..35c7f94 --- /dev/null +++ b/emulator/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/emulator/src/main.rs b/emulator/src/main.rs index b51bf1f..1e85790 100644 --- a/emulator/src/main.rs +++ b/emulator/src/main.rs @@ -22,7 +22,18 @@ use dsa_rs::emulator::{ }, }; -fn main() -> Result<(), Box> { +#[cfg(target_os = "android")] +use winit::platform::android::activity::AndroidApp; + +#[cfg(target_os = "android")] +#[no_mangle] +fn android_main(app: AndroidApp) { + main(app); +} + +fn main( + #[cfg(target_os = "android")] app: AndroidApp, +) -> Result<(), Box> { // Initialize channels and read in configuration. let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel(); let (state_sender, state_reciever) = std::sync::mpsc::channel(); @@ -39,8 +50,13 @@ fn main() -> Result<(), Box> { 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() }; From a878483923c7ce5c876a577f03146be321189eaf Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 22 Jun 2025 02:21:08 +0100 Subject: [PATCH 6/7] emulator: fix build errors in main.rs --- emulator/src/main.rs | 61 ++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/emulator/src/main.rs b/emulator/src/main.rs index 1e85790..e5cff51 100644 --- a/emulator/src/main.rs +++ b/emulator/src/main.rs @@ -22,18 +22,7 @@ use dsa_rs::emulator::{ }, }; -#[cfg(target_os = "android")] -use winit::platform::android::activity::AndroidApp; - -#[cfg(target_os = "android")] -#[no_mangle] -fn android_main(app: AndroidApp) { - main(app); -} - -fn main( - #[cfg(target_os = "android")] app: AndroidApp, -) -> Result<(), Box> { +fn main() -> Result<(), Box> { // Initialize channels and read in configuration. let (cmd_sender, cmd_receiver) = std::sync::mpsc::channel(); let (state_sender, state_reciever) = std::sync::mpsc::channel(); @@ -53,10 +42,6 @@ fn main( #[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() }; @@ -111,3 +96,47 @@ fn setup_ui(cmd_sender: Sender, state_reciever: Receiver) -> Emu ui } + +#[cfg(target_os = "android")] +use winit::platform::android::{EventLoopBuilderExtAndroid, activity::AndroidApp}; + +#[cfg(target_os = "android")] +#[unsafe(no_mangle)] +fn android_main(app: AndroidApp) -> Result<(), Box> { + // 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(()) +} From c7322d81716313b5a6134c36c5869aac9982f434 Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 22 Jun 2025 03:46:42 +0100 Subject: [PATCH 7/7] emulator on android: crashes but APKs are building --- emulator/Cargo.toml | 13 +- emulator/src/lib.rs | 114 ++++++++++++++++++ emulator/src/main.rs | 111 +---------------- .../emulator}/AndroidManifest.xml | 19 ++- resources/emulator/AppIcon.png | Bin 0 -> 2449 bytes resources/emulator/build_android.ps1 | 74 ++++++++++++ resources/emulator/build_android.sh | 46 +++++++ 7 files changed, 259 insertions(+), 118 deletions(-) rename {emulator => resources/emulator}/AndroidManifest.xml (51%) create mode 100644 resources/emulator/AppIcon.png create mode 100644 resources/emulator/build_android.ps1 create mode 100644 resources/emulator/build_android.sh diff --git a/emulator/Cargo.toml b/emulator/Cargo.toml index f4cf832..fb37322 100644 --- a/emulator/Cargo.toml +++ b/emulator/Cargo.toml @@ -7,7 +7,7 @@ default-run = "emulator" [lib] name = "dsa_rs" path = "src/lib.rs" -type = "cdylib" +crate-type = ["cdylib", "rlib"] [[bin]] name = "emulator" @@ -25,17 +25,16 @@ 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"] - -[features] -default = ["config"] -discord-rpc = ["dep:discord-presence"] -config = ["dep:toml", "dep:serde"] diff --git a/emulator/src/lib.rs b/emulator/src/lib.rs index 5374434..19558c4 100644 --- a/emulator/src/lib.rs +++ b/emulator/src/lib.rs @@ -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> { + 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, + state_sender: Sender, + rpc_client: Option>, +) { + 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, + state_reciever: Receiver, +) -> 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 +} diff --git a/emulator/src/main.rs b/emulator/src/main.rs index e5cff51..b074df9 100644 --- a/emulator/src/main.rs +++ b/emulator/src/main.rs @@ -1,26 +1,7 @@ +use std::path::Path; use std::sync::Arc; -use std::{ - path::Path, - sync::mpsc::{Receiver, Sender}, - thread, -}; -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> { // Initialize channels and read in configuration. @@ -34,9 +15,9 @@ fn main() -> Result<(), Box> { let rpc_client = get_rpc_client_or_none(&config, rpc_sender, rpc_reciever)?.map(Arc::new); - setup_emulator(cmd_receiver, state_sender, rpc_client); + 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)] @@ -56,87 +37,3 @@ fn main() -> Result<(), Box> { Ok(()) } - -fn setup_emulator( - cmd_receiver: Receiver, - state_sender: Sender, - rpc_client: Option>, -) { - 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`]. -fn setup_ui(cmd_sender: Sender, state_reciever: Receiver) -> 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 -} - -#[cfg(target_os = "android")] -use winit::platform::android::{EventLoopBuilderExtAndroid, activity::AndroidApp}; - -#[cfg(target_os = "android")] -#[unsafe(no_mangle)] -fn android_main(app: AndroidApp) -> Result<(), Box> { - // 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(()) -} diff --git a/emulator/AndroidManifest.xml b/resources/emulator/AndroidManifest.xml similarity index 51% rename from emulator/AndroidManifest.xml rename to resources/emulator/AndroidManifest.xml index 35c7f94..21292bf 100644 --- a/emulator/AndroidManifest.xml +++ b/resources/emulator/AndroidManifest.xml @@ -1,17 +1,28 @@ + + package="com.dsa.emulator" + android:versionCode="1" + android:versionName="1.0"> + + + + android:icon="@mipmap/ic_launcher" + android:hasCode="false" + android:hardwareAccelerated="true"> + android:exported="true" + android:configChanges="orientation|keyboardHidden|screenSize" + android:screenOrientation="unspecified" + android:launchMode="singleInstance"> @@ -21,4 +32,4 @@ - \ No newline at end of file + diff --git a/resources/emulator/AppIcon.png b/resources/emulator/AppIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..4f58b169aecdf23a0dd89d2eb9d03e1dac8bafc6 GIT binary patch literal 2449 zcmciE`9Bl>1Hke37)8U%T8Z3LVyGDA7$#?=spbsjnrV*J=;N43=|a}Gv^nDwaxIa2 z%~^8hnqnkEu7p-rd>)_QzrTLJKRkbW{R6M$(~i~>;s?Y501~z~xHA9%+P`*+0svY} zeck~8#6oRcBLEPt_!s0*t5hifK!##Ppy0{=6sk|SAD~jHsG!Ti5f^1l{%ZZQMSG_xsqiPM6j^Q?&J;Z+5vI?p!e!eP})-X1&oev%i0DWg@0N?uE!OxzPRRH@0u*H=UsV-3{-b z?`Z8fAx~)J%oxr$CTF$+{pwWPI2hi_@zpcX%_nv>Ye z5aH?l7{mJ0By;j#+}0C(&n*^3_UcmfQ-vt-nJUwB2Q_KM#Jk4YsX`P*-lk}{l)?_O zmS%=~KGj%DNo3Buc;TyS#JBy05((?~3)J#~Q~Y-~g^%~6CG78^5zq$lK!U#0*%Pbn zp|g3GZ`q!lR(o`X_Er<|+RDY25||JY>$;Jq&k5+2eg`HKrYbd;`aM^4GMgXxcXXC} z`g9ML&=yJ^s8w#UeS<;KXnqX1&0oAU;1rAS&JS4|9g=Ne?$0g@db*@KcN|MU&T5nV zG4O#JGPFuHrD1}71qs17@?O}rFn{HAg4kvDwLb?l@Pzk^#ZByV_u{AfBs5NYl?KCN zVN*VWDCYeb!rh1SW%@U1H|H@Ll`)(u^DAfde*1FnJVDDTnrnlXCRvu==Ns6Ie;#ys z)i?d&R5g!kNJTEZBJx6M6mf{##icH?woc=~RttS-6Sj(@JVQ=y&f^iJiTCS<&njAV zl=tVX#4Bb(MVr6uwdXeYz7;;0jVCn!=b^n}b;z2qpS!#N=J^BhU9$34jpC182&~C~ zCwjLxkZd=`KAU@v+BAi@*>Lfo#KObd9ST~X4yi4K8A(3!XIR=_ zqLw6O2@e`|IB4C$``cpnz@9c}fAVUoAnZ1eg+0XHOTx8NQ-s?Lo{bx%Q7lMTF?ghw zTF3`j*SVu0qwG?ow$sW?ob(x*q=j=A&QMw`C0R*bS1dhGhcmjj*|277Tz zP^06nS#zu-jM(dF*Gt!Fv_{V%C2HGc1b^PZa&~*-vy@^!+!-8p`P?aYb9VR?N^GIC%G7fOQMk^MB81p)}-!btS+P0-I-Ha-rYRgXzv-K zRn`@TSQ)ok&C2Yu`PyC)ACoXOla}-b_&i~aqrsvG&+iErtiwGJ_!2c3JM-_dav1e_|+wKWO5 zC;yJM#Hc+#UUR&||5f_Nu!U*27mi(G%axO~YvR76+2VhGd1ncW+qiY3ki?vFF;KDeE1>(Eha8LwFz@eck|t&<4Ut(01qUz4gH=y=0- zua4hxuu&|rhpMM1qr$?NCKT|jJ}w9$YUWUyd>!bfK?p%xa7+syPC9RH+-5}yIS?=i3G=aW}Xu;>TYhq!E5#J}a zeJ2O!Jq$MYstNec_;c-zM5+2%!NT9yRHpKxa;bZ8*4Tuvs_2zXyh+f3{-6n@eEMKI z<`IfG>{U40)yF-2vaJK2wV%K3q7Z%lcgx&kh=ZnW#Fa&427#YG^vdnEZ&E0Rt2v41 zbg7>sM|v{AhBX_es0&RZh}x}N{=YupzdgsAnO0c#2qVR(0001NtsHS>7T(wY0VI@a AegFUf literal 0 HcmV?d00001 diff --git a/resources/emulator/build_android.ps1 b/resources/emulator/build_android.ps1 new file mode 100644 index 0000000..1e11e65 --- /dev/null +++ b/resources/emulator/build_android.ps1 @@ -0,0 +1,74 @@ +#!/usr/bin/env pwsh + +$env:ANDROID_HOME = "C:\Users\jacob\AppData\Local\Android\Sdk" +$TOOL_PREFIX = "$env:ANDROID_HOME\build-tools\35.0.1" + +# Only really works on Windows, for aarch64. + +# Create directories +New-Item -ItemType Directory -Force -Path "..\target\apk_build\lib\arm64-v8a" +New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\values" +New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-hdpi" +New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-mdpi" +New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-xhdpi" +New-Item -ItemType Directory -Force -Path "..\target\apk_build\res\mipmap-xxhdpi" + +# Copy the shared library +Copy-Item "..\target\aarch64-linux-android\release\libdsa_rs.so" "..\target\apk_build\lib\arm64-v8a\" + +# Copy the manifest +Copy-Item "..\resources\emulator\AndroidManifest.xml" "..\target\apk_build\AndroidManifest.xml" + +# Copy the icons +Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-hdpi\ic_launcher.png" +Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-mdpi\ic_launcher.png" +Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-xhdpi\ic_launcher.png" +Copy-Item "..\resources\emulator\AppIcon.png" "..\target\apk_build\res\mipmap-xxhdpi\ic_launcher.png" + +# Create strings.xml +@" + + + DSA Emulator + +"@ | Out-File -FilePath "..\target\apk_build\res\values\strings.xml" -Encoding utf8 + +# Change to build directory +Push-Location "..\target\apk_build" + +try { + # Compile resources + & "$TOOL_PREFIX\aapt2.exe" compile --dir res -o compiled_resources.zip + + # Link resources + & "$TOOL_PREFIX\aapt2.exe" link -o unaligned.apk ` + -I "$env:ANDROID_HOME\platforms\android-35\android.jar" ` + --manifest AndroidManifest.xml ` + compiled_resources.zip + + # Add native libraries to APK + & "C:\Program Files\7-Zip\7z.exe" a -tzip unaligned.apk lib\* + + # Align APK + & "$TOOL_PREFIX\zipalign.exe" -v 4 unaligned.apk aligned.apk + + # Generate debug keystore if it doesn't exist + if (-not (Test-Path "debug.keystore")) { + & keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 -storepass android -keypass android -dname "CN=Android Debug,O=Android,C=US" + } + + # Sign APK + & "$TOOL_PREFIX\apksigner.bat" sign --ks debug.keystore --ks-key-alias androiddebugkey --ks-pass pass:android --key-pass pass:android --out dsa_emulator.apk aligned.apk + + # Copy final APK + Copy-Item "dsa_emulator.apk" "..\dsa_emulator.apk" + + Write-Host "APK created successfully at: ..\target\dsa_emulator.apk" -ForegroundColor Green +} +catch { + Write-Error "Build failed: $_" +} +finally { + # Return to original directory + Pop-Location +} diff --git a/resources/emulator/build_android.sh b/resources/emulator/build_android.sh new file mode 100644 index 0000000..fba7943 --- /dev/null +++ b/resources/emulator/build_android.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +export ANDROID_HOME="/mnt/c/Users/jacob/AppData/Local/Android/Sdk" +export TOOL_PREFIX="$ANDROID_HOME/build-tools/35.0.1" + +# Only really works on Linux, for aarch64. + +mkdir -p ../target/apk_build/lib/arm64-v8a +mkdir -p ../target/apk_build/res/values +mkdir -p ../target/apk_build/res/mipmap-hdpi +mkdir -p ../target/apk_build/res/mipmap-mdpi +mkdir -p ../target/apk_build/res/mipmap-xhdpi +mkdir -p ../target/apk_build/res/mipmap-xxhdpi + +# Copy the shared library. +cp ../target/aarch64-linux-android/release/libdsa_rs.so ../target/apk_build/lib/arm64-v8a/ + +# Copy the manifest. +cp AndroidManifest.xml ../target/apk_build/AndroidManifest.xml + +cat << EOF > ../target/apk_build/res/values/strings.xml + + + DSA Emulator + +EOF + +pushd ../target/apk_build + +$TOOL_PREFIX/aapt2 compile --dir res -o compiled_resources.zip +$TOOL_PREFIX/aapt2 link -o unaligned.apk \ + -I "$ANDROID_HOME/platforms/android-35/android.jar" \ + --manifest AndroidManifest.xml \ + compiled_resources.zip + +zip -r unaligned.apk lib/ + +$TOOL_PREFIX/zipalign -v 4 unaligned.apk aligned.apk + +keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 -storepass android -keypass android -dname "CN=Android Debug,O=Android,C=US" + +$TOOL_PREFIX/apksigner sign --ks debug.keystore --ks-key-alias androiddebugkey --ks-pass pass:android --key-pass pass:android --out dsa_emulator.apk aligned.apk + +cp dsa_emulator.apk ../dsa_emulator.apk + +popd