screenshots!

This commit is contained in:
Alex Janka 2023-10-10 14:15:40 +11:00
parent a74d9e4d1a
commit e53dff0a30
4 changed files with 82 additions and 3 deletions

5
Cargo.lock generated
View file

@ -509,8 +509,10 @@ checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [ dependencies = [
"android-tzdata", "android-tzdata",
"iana-time-zone", "iana-time-zone",
"js-sys",
"num-traits", "num-traits",
"serde", "serde",
"wasm-bindgen",
"windows-targets 0.48.5", "windows-targets 0.48.5",
] ]
@ -1247,12 +1249,15 @@ dependencies = [
name = "gb-emu" name = "gb-emu"
version = "0.4.0" version = "0.4.0"
dependencies = [ dependencies = [
"bytemuck",
"chrono",
"clap", "clap",
"cpal", "cpal",
"ctrlc", "ctrlc",
"futures", "futures",
"gb-emu-lib", "gb-emu-lib",
"gilrs", "gilrs",
"image",
"nokhwa", "nokhwa",
"raw-window-handle", "raw-window-handle",
"send_wrapper", "send_wrapper",

View file

@ -31,3 +31,6 @@ winit = "0.28"
winit_input_helper = "0.14" winit_input_helper = "0.14"
raw-window-handle = { version = "0.5", optional = true } raw-window-handle = { version = "0.5", optional = true }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
image = { version = "0.24", default-features = false, features = ["png"] }
bytemuck = "1.14"
chrono = "0.4"

View file

@ -16,7 +16,7 @@ use gilrs::Gilrs;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
path::PathBuf, path::PathBuf,
sync::mpsc::channel, sync::{mpsc::channel, OnceLock},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use window::{WindowManager, WindowRenderer}; use window::{WindowManager, WindowRenderer};
@ -81,13 +81,31 @@ struct Args {
#[serde(default)] #[serde(default)]
pub struct StandaloneConfig { pub struct StandaloneConfig {
scale_factor: usize, scale_factor: usize,
group_screenshots_by_rom: bool,
} }
impl Default for StandaloneConfig { impl Default for StandaloneConfig {
fn default() -> Self { fn default() -> Self {
Self { scale_factor: 3 } Self {
scale_factor: 3,
group_screenshots_by_rom: true,
} }
} }
}
#[allow(dead_code)]
struct Configs {
standalone_config: StandaloneConfig,
emu_config: gb_emu_lib::config::Config,
config_dir: PathBuf,
rom_title: String,
}
static CONFIGS: OnceLock<Configs> = OnceLock::new();
fn access_config<'a>() -> &'a Configs {
CONFIGS.get().expect("accessed config before it was set!")
}
fn main() { fn main() {
let args = Args::parse(); let args = Args::parse();
@ -168,6 +186,13 @@ fn run(args: Args) -> ! {
None None
}; };
CONFIGS.get_or_init(|| Configs {
standalone_config,
emu_config: config.clone(),
config_dir: config_manager.dir(),
rom_title: rom.get_title().to_owned(),
});
let options = let options =
EmulatorOptions::new_with_config(config, config_manager.dir(), window, rom, output) EmulatorOptions::new_with_config(config, config_manager.dir(), window, rom, output)
.with_serial_target(if args.ascii { .with_serial_target(if args.ascii {

View file

@ -13,6 +13,7 @@ use gilrs::{
ff::{BaseEffect, BaseEffectType, EffectBuilder, Replay, Ticks}, ff::{BaseEffect, BaseEffectType, EffectBuilder, Replay, Ticks},
Button, Gilrs, Button, Gilrs,
}; };
use image::ImageBuffer;
#[cfg(feature = "vulkan")] #[cfg(feature = "vulkan")]
use raw_window_handle::HasRawDisplayHandle; use raw_window_handle::HasRawDisplayHandle;
use winit::{ use winit::{
@ -24,6 +25,8 @@ use winit::{
}; };
use winit_input_helper::WinitInputHelper; use winit_input_helper::WinitInputHelper;
use crate::access_config;
pub struct WindowInfo { pub struct WindowInfo {
id: WindowId, id: WindowId,
data: Arc<WindowData>, data: Arc<WindowData>,
@ -33,6 +36,7 @@ struct WindowData {
renderer: Mutex<RendererBackend>, renderer: Mutex<RendererBackend>,
window: Window, window: Window,
rendered_size: RwLock<(u32, u32)>, rendered_size: RwLock<(u32, u32)>,
last_buf: Mutex<Vec<[u8; 4]>>,
} }
pub struct WindowManager { pub struct WindowManager {
@ -129,7 +133,44 @@ impl WindowManagerData {
control_flow.set_wait(); control_flow.set_wait();
if let Ok(mut i) = self.input.lock() { if let Ok(mut i) = self.input.lock() {
i.update(&event); if i.update(&event) && i.key_pressed(VirtualKeyCode::Space) {
self.windows.iter().for_each(|(_id, window)| {
if let Ok(buf) = window.last_buf.lock() {
if let Ok(size) = window.rendered_size.read() {
let image = ImageBuffer::<image::Rgba<u8>, _>::from_raw(
size.0,
size.1,
bytemuck::cast_slice(buf.as_ref()),
)
.unwrap();
let configs = access_config();
let screenshot_dir =
if configs.standalone_config.group_screenshots_by_rom {
configs
.config_dir
.join(format!("screenshots/{}", configs.rom_title))
} else {
configs.config_dir.clone()
};
std::fs::create_dir_all(&screenshot_dir)
.expect("could not create screenshot directory!");
let screenshot_path = screenshot_dir.join(format!(
"{} - {}.png",
chrono::DateTime::<chrono::Local>::from(
std::time::SystemTime::now()
)
.to_rfc3339(),
configs.rom_title,
));
image
.save(screenshot_path)
.expect("Could not save screenshot!");
}
}
});
}
} }
match event { match event {
@ -218,6 +259,7 @@ impl WindowRenderer {
renderer, renderer,
window, window,
rendered_size: RwLock::new((1, 1)), rendered_size: RwLock::new((1, 1)),
last_buf: Mutex::new(Vec::new()),
}); });
let info = WindowInfo { let info = WindowInfo {
@ -277,6 +319,10 @@ impl Renderer<[u8; 4]> for WindowRenderer {
data.new_frame(buffer); data.new_frame(buffer);
} }
self.data.window.request_redraw(); self.data.window.request_redraw();
if let Ok(mut last_buf) = self.data.last_buf.lock() {
last_buf.resize(buffer.len(), [0; 4]);
last_buf.copy_from_slice(buffer);
}
} }
fn set_title(&mut self, title: String) { fn set_title(&mut self, title: String) {