screenshots!
This commit is contained in:
parent
a74d9e4d1a
commit
e53dff0a30
4 changed files with 82 additions and 3 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue