diff options
-rw-r--r-- | Cargo.lock | 89 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | rust-toolchain.toml | 2 | ||||
-rw-r--r-- | src/bin/br/main.rs | 34 | ||||
-rw-r--r-- | src/devices/file_device.rs | 143 | ||||
-rw-r--r-- | src/devices/input_device.rs | 1 | ||||
-rw-r--r-- | src/emulators/graphical_emulator.rs | 51 | ||||
-rw-r--r-- | src/emulators/headless_emulator.rs | 2 | ||||
-rw-r--r-- | src/emulators/mod.rs | 3 | ||||
-rw-r--r-- | src/lib.rs | 22 | ||||
-rw-r--r-- | src/types/controller.rs | 6 |
11 files changed, 268 insertions, 98 deletions
@@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.3.3", "once_cell", "version_check", "zerocopy", @@ -87,8 +87,8 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bedrock-asm" -version = "1.0.1" -source = "git+git://benbridle.com/bedrock-asm?tag=v1.0.1#1d455e11d554c10ec993c2faf43a2945311397d0" +version = "1.0.2" +source = "git+git://benbridle.com/bedrock-asm?tag=v1.0.2#c4f161b44f3eb2811438b17d55d3d36767aee790" dependencies = [ "assembler", "indexmap", @@ -103,17 +103,19 @@ source = "git+git://benbridle.com/bedrock-core?tag=v1.0.0#9e6a17a24c5dd5748270d0 [[package]] name = "bedrock-pc" -version = "1.0.0" +version = "1.2.0" dependencies = [ "bedrock-asm", "bedrock-core", "chrono", + "dirs-next", "geometry", "gilrs", "inked", "log 2.0.0", "phosphor", "switchboard", + "vagabond", "windows 0.58.0", ] @@ -368,6 +370,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] +name = "dirs-next" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] name = "dispatch" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -510,6 +533,17 @@ dependencies = [ [[package]] name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" @@ -517,7 +551,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -666,7 +700,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom", + "getrandom 0.3.3", "libc", ] @@ -1083,8 +1117,8 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phosphor" -version = "3.3.0" -source = "git+git://benbridle.com/phosphor?tag=v3.3.0#92a62768fe6be9f72716c14f427767cce5c03b2f" +version = "3.3.2" +source = "git+git://benbridle.com/phosphor?tag=v3.3.2#37d89379b77ea9032c83da570e4382834ae148c6" dependencies = [ "buffer", "event-queue", @@ -1212,6 +1246,17 @@ dependencies = [ ] [[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror", +] + +[[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1508,6 +1553,12 @@ dependencies = [ [[package]] name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" @@ -1716,6 +1767,22 @@ dependencies = [ ] [[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1725,6 +1792,12 @@ dependencies = [ ] [[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] name = "windows" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,25 +1,30 @@ [package] name = "bedrock-pc" -version = "1.0.0" +version = "1.2.0" authors = ["Ben Bridle"] edition = "2024" description = "Bedrock emulator" [dependencies] -bedrock-asm = { git = "git://benbridle.com/bedrock-asm", tag = "v1.0.1" } +bedrock-asm = { git = "git://benbridle.com/bedrock-asm", tag = "v1.0.2" } bedrock-core = { git = "git://benbridle.com/bedrock-core", tag = "v1.0.0" } -phosphor = { git = "git://benbridle.com/phosphor", tag = "v3.3.0" } +phosphor = { git = "git://benbridle.com/phosphor", tag = "v3.3.2" } geometry = { git = "git://benbridle.com/geometry", tag = "v1.0.0" } inked = { git = "git://benbridle.com/inked", tag = "v1.0.0" } log = { git = "git://benbridle.com/log", tag = "v2.0.0" } switchboard = { git = "git://benbridle.com/switchboard", tag = "v2.1.0" } +vagabond = { git = "git://benbridle.com/vagabond", tag = "v1.1.1" } chrono = { version = "0.4.38" } -gilrs = "0.11.0" +gilrs = { version = "0.11.0", optional = true } +dirs-next = "1.0.2" [target.'cfg(target_os = "windows")'.dependencies] windows = { version = "0.58.0", features = ["Win32_Storage_FileSystem"] } +[features] +default = ["gamepad"] +gamepad = ["dep:gilrs"] [profile.release] lto=true diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/src/bin/br/main.rs b/src/bin/br/main.rs index 81c5ec9..da11a18 100644 --- a/src/bin/br/main.rs +++ b/src/bin/br/main.rs @@ -55,6 +55,7 @@ fn main() { args.named("size").short('s'); args.named("decode-stdin").short('i'); args.named("encode-stdout").short('o'); + args.named("trust-files"); args.raise_errors(); let source = args.get("source").as_path_opt(); @@ -71,6 +72,7 @@ fn main() { }; let decode_stdin = args.get("decode-stdin").as_bool(); let encode_stdout = args.get("encode-stdout").as_bool(); + let trust_files = args.get("trust-files").as_bool(); // ----------------------------------------------------------------------- @@ -78,7 +80,9 @@ fn main() { let mut title = String::from("Bedrock program"); let mut icon = None; - if let Some(metadata) = Metadata::from(&bytecode) { + let metadata = Metadata::from(&bytecode); + + if let Some(ref metadata) = metadata { let name = metadata.name().unwrap_or("unnamed".to_string()); let authors = metadata.authors().unwrap_or_else(Vec::new); let mut metadata_string = format!("Program is '{name}'"); @@ -110,16 +114,26 @@ fn main() { path.add_extension("sym"); path }); + let name = metadata.and_then(|m| m.name()).and_then(|n| match n.split_once('/') { + Some((name, _)) => Some(name.to_string()), + None => Some(n), + }); + let identifier = name.as_ref().and_then( + |n| Some(n.to_lowercase().chars().filter_map( + |c| c.is_alphanumeric().then_some(c) + ).collect()) + ); + let config = EmulatorConfig { dimensions, fullscreen, zoom, palette, show_cursor, - decode_stdin, encode_stdout, - symbols_path, title, icon, + decode_stdin, encode_stdout, trust_files, + symbols_path, name, identifier, title, icon, }; - if let Ok(phosphor) = Phosphor::new() { - match mode { + match Phosphor::new() { + Ok(phosphor) => match mode { Mode::Dynamic => { - info!("Starting graphical emulator"); + info!("Starting graphical emulator (hidden)"); let mut emulator = GraphicalEmulator::new(config, debug); emulator.load_program(&bytecode); emulator.run(phosphor, false); @@ -128,7 +142,7 @@ fn main() { info!("Starting graphical emulator"); let mut emulator = GraphicalEmulator::new(config, debug); emulator.load_program(&bytecode); - emulator.run(phosphor, false); + emulator.run(phosphor, true); } Mode::Headless => { info!("Starting headless emulator"); @@ -137,9 +151,9 @@ fn main() { emulator.run(); } } - } else { - match mode { + Err(err) => match mode { Mode::Dynamic => { + eprintln!("EventLoopError: {err:?}"); info!("Could not start graphical event loop"); info!("Starting headless emulator"); let mut emulator = HeadlessEmulator::new(&config, debug); @@ -147,6 +161,7 @@ fn main() { emulator.run(); } Mode::Graphical => { + eprintln!("EventLoopError: {err:?}"); fatal!("Could not start graphical event loop"); } Mode::Headless => { @@ -210,6 +225,7 @@ Switches: --debug, (-d) Show debug information while the program is running --decode-stdin (-i) Decode transmissions on standard input from text lines. --encode-stdout (-o) Encode transmissions on standard output as text lines. + --trust-files Give the program unrestricted access to the file system. --help (-h) Print this help information --verbose, (-v) Print additional information --version Print the program version and exit diff --git a/src/devices/file_device.rs b/src/devices/file_device.rs index ff5629b..83f0a56 100644 --- a/src/devices/file_device.rs +++ b/src/devices/file_device.rs @@ -9,13 +9,14 @@ pub struct FileDevice { pub action_buffer: BedrockPathBuffer, pub path_buffer: BedrockPathBuffer, - pub entry: Option<(Entry, BedrockFilePath)>, - pub cached_dir: Option<(Entry, BedrockFilePath)>, + pub entry: Option<(Entry, BedrockFilePath, Instant)>, + pub cached_dir: Option<(Entry, BedrockFilePath, Instant)>, - pub success: bool, + pub error: bool, pub pointer_write: u32, pub length_write: u32, + pub enable: bool, pub enable_read: bool, pub enable_write: bool, pub enable_create: bool, @@ -26,9 +27,10 @@ pub struct FileDevice { impl Device for FileDevice { fn read(&mut self, port: u8) -> u8 { + if !self.enable { return 0x00; } match port { 0x0 => read_b!(self.entry.is_some()), - 0x1 => read_b!(self.success), + 0x1 => read_b!(std::mem::take(&mut self.error)), 0x2 => self.read_byte(), 0x3 => self.read_byte(), 0x4 => self.path_buffer.read(), @@ -48,6 +50,7 @@ impl Device for FileDevice { } fn write(&mut self, port: u8, value: u8) -> Option<Signal> { + if !self.enable { return None; } match port { 0x0 => self.write_to_entry_port(value), 0x1 => self.write_to_action_port(value), @@ -81,20 +84,35 @@ impl Device for FileDevice { impl FileDevice { - pub fn new() -> Self { + pub fn new(config: &EmulatorConfig) -> Self { #[cfg(target_family = "unix")] let default_base: PathBuf = PathBuf::from("/"); #[cfg(target_family = "windows")] let default_base: PathBuf = PathBuf::from(""); + let current_dir = match std::env::current_dir() { + Ok(dir) => PathBuf::from(dir), + Err(_) => PathBuf::from(""), + }; + + let (enable, base_path, default_path) = if config.trust_files { + (true, default_base, current_dir) + } else if let Some(config_dir) = dirs_next::config_dir() { + let bedrock_dir = config_dir.join("bedrock"); + let identifier = config.identifier.clone().unwrap_or("default".to_string()); + let sandbox_dir = bedrock_dir.join(identifier); + vagabond::make_directory(&sandbox_dir).unwrap(); + (true, sandbox_dir.clone(), sandbox_dir) + } else { + error!("Could not determine sandbox path for file device"); + (false, default_base, current_dir) + }; + // TODO: I'm not at all confident that the default path is correct // when not being set as the current directory. Self { - base_path: default_base, - default_path: match std::env::current_dir() { - Ok(dir) => PathBuf::from(dir), - Err(_) => PathBuf::from(""), - }, + base_path, + default_path, entry_buffer: BedrockPathBuffer::new(), action_buffer: BedrockPathBuffer::new(), @@ -103,10 +121,11 @@ impl FileDevice { entry: None, cached_dir: None, - success: false, + error: false, pointer_write: 0, length_write: 0, + enable, enable_read: true, enable_write: true, enable_create: true, @@ -115,6 +134,12 @@ impl FileDevice { } } + pub fn check_success(&mut self, success: bool) { + if !success { + self.error = true; + } + } + /// Safely close the current entry, cleaning up entry variables. pub fn close(&mut self) { self.entry_buffer.clear(); @@ -122,10 +147,10 @@ impl FileDevice { self.path_buffer.clear(); self.flush(); - if let Some((Entry::Directory(mut dir), path)) = std::mem::take(&mut self.entry) { + if let Some((Entry::Directory(mut dir), path, time)) = std::mem::take(&mut self.entry) { // Prevent the selected child from persisting when loading from cache. dir.deselect_child(); - self.cached_dir = Some((Entry::Directory(dir), path)); + self.cached_dir = Some((Entry::Directory(dir), path, time)); } } @@ -141,17 +166,17 @@ impl FileDevice { if let Ok(file) = open_result { self.close(); self.path_buffer.populate(path.as_buffer()); - self.entry = Some((Entry::File(BufferedFile::new(file)), path)); + self.entry = Some((Entry::File(BufferedFile::new(file)), path, Instant::now())); return Ok(()); }; } Some(EntryType::Directory) => { - // Attempt to use the cached directory. - if let Some((dir, cached_path)) = std::mem::take(&mut self.cached_dir) { - if cached_path == path { + // Attempt to use the cached directory if not too old. + if let Some((dir, cached_path, time)) = std::mem::take(&mut self.cached_dir) { + if cached_path == path && time.elapsed() < Duration::from_secs(1) { self.close(); self.path_buffer.populate(cached_path.as_buffer()); - self.entry = Some((dir, cached_path)); + self.entry = Some((dir, cached_path, time)); return Ok(()); } } @@ -159,7 +184,7 @@ impl FileDevice { if let Some(listing) = DirectoryListing::from_path(&path) { self.close(); self.path_buffer.populate(path.as_buffer()); - self.entry = Some((Entry::Directory(listing), path)); + self.entry = Some((Entry::Directory(listing), path, Instant::now())); return Ok(()); }; } @@ -173,10 +198,16 @@ impl FileDevice { pub fn write_to_entry_port(&mut self, byte: u8) { if let Some(buffer) = self.entry_buffer.write(byte) { self.close(); - match BedrockFilePath::from_buffer(buffer, &self.base_path) { - Some(path) => self.success = self.open(path).is_ok(), - None => self.success = false, - }; + // Attempt to open file if buffer was not empty. + if buffer[0] != 0 { + let success = match BedrockFilePath::from_buffer(buffer, &self.base_path) { + Some(path) => self.open(path).is_ok(), + None => false, + }; + self.check_success(success); + } else { + self.check_success(true); + } } } @@ -185,22 +216,23 @@ impl FileDevice { if let Some(buffer) = self.action_buffer.write(byte) { let destination_blank = buffer[0] == 0x00; let destination = BedrockFilePath::from_buffer(buffer, &self.base_path); - self.success = false; - if let Some((_, source)) = &self.entry { + if let Some((_, source, _)) = &self.entry { if destination_blank { if self.enable_delete { - self.success = delete_entry(&source.as_path()); + self.check_success(delete_entry(&source.as_path())); } } else if let Some(dest) = destination { if self.enable_move { - self.success = move_entry(&source.as_path(), &dest.as_path()); + self.check_success(move_entry(&source.as_path(), &dest.as_path())); } } } else if let Some(dest) = destination { if self.enable_create { - self.success = create_file(&dest.as_path()); + self.check_success(create_file(&dest.as_path())); } + } else { + self.check_success(false); } self.close(); } @@ -208,35 +240,38 @@ impl FileDevice { /// Attempt to open the parent directory of the current entry. pub fn ascend_to_parent(&mut self) { - if let Some((_, path)) = &self.entry { - match path.parent() { - Some(parent) => self.success = self.open(parent).is_ok(), - None => self.success = false, + if let Some((_, path, _)) = &self.entry { + let success = match path.parent() { + Some(parent) => self.open(parent).is_ok(), + None => false, }; + self.check_success(success); } else { - match BedrockFilePath::from_path(&self.default_path, &self.base_path) { - Some(default) => self.success = self.open(default).is_ok(), - None => self.success = false, + let success = match BedrockFilePath::from_path(&self.default_path, &self.base_path) { + Some(default) => self.open(default).is_ok(), + None => false, }; + self.check_success(success); } } /// Attempt to open the selected child of the current directory. pub fn descend_to_child(&mut self) { - if let Some((Entry::Directory(dir), _)) = &self.entry { - match dir.child_path() { - Some(child) => self.success = self.open(child).is_ok(), - None => self.success = false, + if let Some((Entry::Directory(dir), _, _)) = &self.entry { + let success = match dir.child_path() { + Some(child) => self.open(child).is_ok(), + None => false, }; + self.check_success(success); } else { - self.success = false; + self.check_success(false); } } /// Return true if the current entry is a directory. pub fn entry_type(&self) -> bool { match self.entry { - Some((Entry::Directory(_), _)) => true, + Some((Entry::Directory(_), _, _)) => true, _ => false, } } @@ -244,13 +279,13 @@ impl FileDevice { /// Read a byte from the path buffer of the selected child. pub fn read_child_path(&mut self) -> u8 { match &mut self.entry { - Some((Entry::Directory(dir), _)) => dir.child_path_buffer().read(), + Some((Entry::Directory(dir), _, _)) => dir.child_path_buffer().read(), _ => 0, } } pub fn set_child_path(&mut self, byte: u8) { - if let Some((Entry::Directory(dir), _)) = &mut self.entry { + if let Some((Entry::Directory(dir), _, _)) = &mut self.entry { dir.child_path_buffer().set_pointer(byte); } } @@ -258,7 +293,7 @@ impl FileDevice { /// Return true if the selected child is a directory. pub fn child_type(&self) -> bool { match &self.entry { - Some((Entry::Directory(dir), _)) => match dir.child_type() { + Some((Entry::Directory(dir), _, _)) => match dir.child_type() { Some(EntryType::Directory) => true, _ => false, } @@ -269,7 +304,7 @@ impl FileDevice { /// Read a byte from the current file. pub fn read_byte(&mut self) -> u8 { match &mut self.entry { - Some((Entry::File(file), _)) => file.read(), + Some((Entry::File(file), _, _)) => file.read(), _ => 0, } } @@ -277,44 +312,44 @@ impl FileDevice { /// Writes a byte to the currently-open file. pub fn write_byte(&mut self, byte: u8) { match &mut self.entry { - Some((Entry::File(file), _)) => file.write(byte), + Some((Entry::File(file), _, _)) => file.write(byte), _ => (), } } pub fn pointer(&mut self) -> u32 { match &mut self.entry { - Some((Entry::File(file), _)) => file.pointer(), - Some((Entry::Directory(dir), _)) => dir.selected(), + Some((Entry::File(file), _, _)) => file.pointer(), + Some((Entry::Directory(dir), _, _)) => dir.selected(), _ => 0, } } pub fn commit_pointer(&mut self) { match &mut self.entry { - Some((Entry::File(file), _)) => file.set_pointer(self.pointer_write), - Some((Entry::Directory(dir), _)) => dir.set_selected(self.pointer_write), + Some((Entry::File(file), _, _)) => file.set_pointer(self.pointer_write), + Some((Entry::Directory(dir), _, _)) => dir.set_selected(self.pointer_write), _ => (), } } pub fn length(&mut self) -> u32 { match &mut self.entry { - Some((Entry::File(file), _)) => file.length(), - Some((Entry::Directory(dir), _)) => dir.length(), + Some((Entry::File(file), _, _)) => file.length(), + Some((Entry::Directory(dir), _, _)) => dir.length(), _ => 0, } } pub fn commit_length(&mut self) { match &mut self.entry { - Some((Entry::File(file), _)) => file.set_length(self.length_write), + Some((Entry::File(file), _, _)) => file.set_length(self.length_write), _ => (), } } pub fn flush(&mut self) { - if let Some((Entry::File(buffered_file), _)) = &mut self.entry { + if let Some((Entry::File(buffered_file), _, _)) = &mut self.entry { let _ = buffered_file; } } diff --git a/src/devices/input_device.rs b/src/devices/input_device.rs index d4e0cb0..3ebeb4c 100644 --- a/src/devices/input_device.rs +++ b/src/devices/input_device.rs @@ -132,6 +132,7 @@ impl InputDevice { } } + #[cfg(feature = "gamepad")] pub fn on_gamepad_event(&mut self, event: gilrs::Event) { if let Some(g) = self.gamepad_1.register(event.id) { self.wake |= g.process_event(&event); return; } diff --git a/src/emulators/graphical_emulator.rs b/src/emulators/graphical_emulator.rs index 2680d3f..503a8f4 100644 --- a/src/emulators/graphical_emulator.rs +++ b/src/emulators/graphical_emulator.rs @@ -1,14 +1,13 @@ use crate::*; -use gilrs::Gilrs; - - pub struct GraphicalEmulator { pub br: BedrockEmulator<GraphicalDeviceBus>, pub debug: DebugState, + #[cfg(feature = "gamepad")] pub gilrs: Option<Gilrs>, pub fullscreen: bool, + pub visible: bool, pub scale: u32, pub render_mark: Instant, // last time screen was rendered pub frame_mark: Instant, // refreshes when clean @@ -18,18 +17,18 @@ pub struct GraphicalEmulator { impl GraphicalEmulator { pub fn new(config: EmulatorConfig, debug: bool) -> Self { - let gilrs = match Gilrs::new() { - Ok(gilrs) => Some(gilrs), - Err(err) => { - info!("Could not start gamepad listener: {}", err); - None - } - }; - Self { br: BedrockEmulator::new(GraphicalDeviceBus::new(&config)), debug: DebugState::new(debug, config.symbols_path.as_ref()), - gilrs, + + #[cfg(feature = "gamepad")] + gilrs: match Gilrs::new() { + Ok(gilrs) => Some(gilrs), + Err(err) => { + info!("Could not start gamepad listener: {}", err); + None + } + }, fullscreen: config.fullscreen, scale: config.zoom.into(), @@ -37,6 +36,7 @@ impl GraphicalEmulator { render_mark: Instant::now(), frame_mark: Instant::now(), config, + visible: false, } } @@ -44,7 +44,8 @@ impl GraphicalEmulator { self.br.core.mem.load_program(bytecode); } - pub fn run(self, mut phosphor: Phosphor, visible: bool) { + pub fn run(mut self, mut phosphor: Phosphor, visible: bool) { + self.visible = visible; let window = WindowBuilder { dimensions: Some(self.dimensions()), size_bounds: Some(self.size_bounds()), @@ -59,7 +60,6 @@ impl GraphicalEmulator { program: Box::new(self), visible, }; - phosphor.add_window(window); phosphor.run().unwrap(); } @@ -112,7 +112,7 @@ impl GraphicalDeviceBus { input: InputDevice::new(), screen: ScreenDevice::new(&config), stream: StreamDevice::new(&config), - file: FileDevice::new(), + file: FileDevice::new(&config), wake_queue: WakeQueue::new(), } } @@ -164,13 +164,13 @@ impl WindowProgram for GraphicalEmulator { Event::FocusChange(_) => (), Event::Initialise => (), Event::ScrollLines { axis, distance } => match axis { - Axis::Horizontal => self.br.dev.input.on_horizontal_scroll(distance * 20.0), - Axis::Vertical => self.br.dev.input.on_vertical_scroll(distance * 20.0), - } - Event::ScrollPixels { axis, distance } => match axis { Axis::Horizontal => self.br.dev.input.on_horizontal_scroll(distance), Axis::Vertical => self.br.dev.input.on_vertical_scroll(distance), } + Event::ScrollPixels { axis, distance } => match axis { + Axis::Horizontal => self.br.dev.input.on_horizontal_scroll(distance / 20.0), + Axis::Vertical => self.br.dev.input.on_vertical_scroll(distance / 20.0), + } Event::Close => (), Event::KeyboardInput { key, action } => { @@ -203,6 +203,8 @@ impl WindowProgram for GraphicalEmulator { fn process(&mut self, requests: &mut EventWriter<Request>) { self.br.dev.stream.flush(); + + #[cfg(feature = "gamepad")] if let Some(gilrs) = &mut self.gilrs { while let Some(event) = gilrs.next_event() { self.br.dev.input.on_gamepad_event(event); @@ -233,9 +235,6 @@ impl WindowProgram for GraphicalEmulator { if let Some(signal) = self.br.evaluate(BATCH_SIZE, self.debug.enabled) { match signal { Signal::Break => { - if self.br.dev.input.accessed || self.br.dev.screen.accessed { - requests.write(Request::SetVisible(true)); - } } Signal::Fork | Signal::Reset => { self.br.reset(); @@ -260,6 +259,14 @@ impl WindowProgram for GraphicalEmulator { } } + if !self.visible { + if self.br.dev.input.accessed || self.br.dev.screen.accessed { + info!("Making window visible"); + requests.write(Request::SetVisible(true)); + self.visible = true; + } + } + if std::mem::take(&mut self.br.dev.screen.dirty_dimensions) { requests.write(Request::SetSizeBounds(self.size_bounds())); } diff --git a/src/emulators/headless_emulator.rs b/src/emulators/headless_emulator.rs index cac58cf..770bae3 100644 --- a/src/emulators/headless_emulator.rs +++ b/src/emulators/headless_emulator.rs @@ -70,7 +70,7 @@ impl HeadlessDeviceBus { math: MathDevice::new(), clock: ClockDevice::new(), stream: StreamDevice::new(&config), - file: FileDevice::new(), + file: FileDevice::new(&config), wake_queue: WakeQueue::new(), } } diff --git a/src/emulators/mod.rs b/src/emulators/mod.rs index 8f04e4d..d4a58f9 100644 --- a/src/emulators/mod.rs +++ b/src/emulators/mod.rs @@ -15,7 +15,10 @@ pub struct EmulatorConfig { pub show_cursor: bool, pub decode_stdin: bool, pub encode_stdout: bool, + pub trust_files: bool, pub symbols_path: Option<PathBuf>, + pub name: Option<String>, + pub identifier: Option<String>, pub title: String, pub icon: Option<Icon>, } @@ -29,3 +29,25 @@ pub const DEFAULT_SCREEN_SCALE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked( pub type ScreenPosition = geometry::Point<u16>; pub type ScreenDimensions = geometry::Dimensions<u16>; + + +pub fn run_program(bytecode: &[u8], config: EmulatorConfig) { + let mut args = switchboard::Switchboard::from_env(); + args.named("verbose").short('v'); + if args.get("verbose").as_bool() { + log::set_log_level(log::LogLevel::Info); + } + + match Phosphor::new() { + Ok(phosphor) => { + info!("Starting graphical emulator"); + let mut emulator = GraphicalEmulator::new(config, false); + emulator.load_program(&bytecode); + emulator.run(phosphor, true); + } + Err(err) => { + eprintln!("EventLoopError: {err:?}"); + fatal!("Could not start graphical event loop"); + } + } +} diff --git a/src/types/controller.rs b/src/types/controller.rs index 76b77e3..42d3f8c 100644 --- a/src/types/controller.rs +++ b/src/types/controller.rs @@ -1,11 +1,15 @@ use crate::*; +#[cfg(feature = "gamepad")] pub use gilrs::{Gilrs, GamepadId}; pub struct OwnedGamepad { tag: usize, + #[cfg(feature = "gamepad")] id: Option<GamepadId>, + #[cfg(not(feature = "gamepad"))] + id: Option<()>, gamepad: Gamepad, } @@ -15,6 +19,7 @@ impl OwnedGamepad { } /// Returns Some if the ID owns this gamepad. + #[cfg(feature = "gamepad")] pub fn register(&mut self, new_id: GamepadId) -> Option<&mut Gamepad> { if let Some(id) = self.id { match id == new_id { @@ -102,6 +107,7 @@ impl Gamepad { } // Returns true if the state changed. + #[cfg(feature = "gamepad")] pub fn process_event(&mut self, event: &gilrs::Event) -> bool { macro_rules! schmitt { ($name_neg:ident, $name_pos:ident, $v:expr) => {{ |