Support screenshots
This commit is contained in:
+75
-3
@@ -6,11 +6,12 @@ use crate::{
|
||||
input::{Command, ShortcutProvider},
|
||||
persistence::Persistence,
|
||||
};
|
||||
use anyhow::Context as _;
|
||||
use egui::{
|
||||
Align2, Button, CentralPanel, Color32, Context, Direction, Frame, TopBottomPanel, Ui, Vec2,
|
||||
ViewportBuilder, ViewportCommand, ViewportId, Window, menu,
|
||||
};
|
||||
use egui_toast::{Toast, Toasts};
|
||||
use egui_toast::{Toast, ToastKind, ToastOptions, Toasts};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use winit::event_loop::EventLoopProxy;
|
||||
|
||||
@@ -69,7 +70,7 @@ impl GameWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn show_menu(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
fn show_menu(&mut self, ctx: &Context, ui: &mut Ui, toasts: &mut Toasts) {
|
||||
let state = self.client.emulator_state();
|
||||
let is_ready = self.client.sim_state(self.sim_id) == SimState::Ready;
|
||||
let can_pause = is_ready && state == EmulatorState::Running;
|
||||
@@ -113,6 +114,16 @@ impl GameWindow {
|
||||
self.client
|
||||
.send_command(EmulatorCommand::SetSpeed(speed as f64));
|
||||
}
|
||||
Command::Screenshot => {
|
||||
let autopause = state == EmulatorState::Running && can_pause;
|
||||
if autopause {
|
||||
self.client.send_command(EmulatorCommand::Pause);
|
||||
}
|
||||
pollster::block_on(self.take_screenshot(toasts));
|
||||
if autopause {
|
||||
self.client.send_command(EmulatorCommand::Resume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +189,17 @@ impl GameWindow {
|
||||
self.client.send_command(EmulatorCommand::FrameAdvance);
|
||||
ui.close_menu();
|
||||
}
|
||||
ui.separator();
|
||||
if ui
|
||||
.add_enabled(
|
||||
is_ready,
|
||||
self.button_for(ui.ctx(), "Screenshot", Command::Screenshot),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
pollster::block_on(self.take_screenshot(toasts));
|
||||
ui.close_menu();
|
||||
}
|
||||
});
|
||||
ui.menu_button("Options", |ui| self.show_options_menu(ctx, ui));
|
||||
ui.menu_button("Multiplayer", |ui| {
|
||||
@@ -262,6 +284,56 @@ impl GameWindow {
|
||||
});
|
||||
}
|
||||
|
||||
async fn take_screenshot(&self, toasts: &mut Toasts) {
|
||||
match self.try_take_screenshot().await {
|
||||
Ok(Some(path)) => {
|
||||
toasts.add(
|
||||
Toast::new()
|
||||
.kind(ToastKind::Info)
|
||||
.options(ToastOptions::default().duration_in_seconds(5.0))
|
||||
.text(format!("Saved to {path}")),
|
||||
);
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(error) => {
|
||||
toasts.add(
|
||||
Toast::new()
|
||||
.kind(ToastKind::Error)
|
||||
.options(ToastOptions::default().duration_in_seconds(5.0))
|
||||
.text(format!("{error:#}")),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_take_screenshot(&self) -> anyhow::Result<Option<String>> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.client
|
||||
.send_command(EmulatorCommand::Screenshot(self.sim_id, tx));
|
||||
let bytes = rx.await.context("Could not take screenshot")?;
|
||||
let file = rfd::AsyncFileDialog::new()
|
||||
.add_filter("PNG images", &["png"])
|
||||
.set_file_name("screenshot.png")
|
||||
.save_file()
|
||||
.await;
|
||||
let Some(file) = file else {
|
||||
return Ok(None);
|
||||
};
|
||||
if bytes.len() != 384 * 224 * 2 {
|
||||
anyhow::bail!("Unexpected screenshot size");
|
||||
}
|
||||
let mut screencap = image::GrayImage::new(384 * 2, 224);
|
||||
for (index, pixel) in bytes.into_iter().enumerate() {
|
||||
let x = (index / 2) % 384 + ((index % 2) * 384);
|
||||
let y = (index / 2) / 384;
|
||||
screencap.put_pixel(x as u32, y as u32, image::Luma([pixel]));
|
||||
}
|
||||
screencap
|
||||
.save(&file.path())
|
||||
.context("Could not save screenshot")?;
|
||||
Ok(Some(file.path().display().to_string()))
|
||||
}
|
||||
|
||||
fn show_options_menu(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
ui.menu_button("Video", |ui| {
|
||||
ui.menu_button("Screen Size", |ui| {
|
||||
@@ -478,7 +550,7 @@ impl AppWindow for GameWindow {
|
||||
.exact_height(22.0)
|
||||
.show(ctx, |ui| {
|
||||
menu::bar(ui, |ui| {
|
||||
self.show_menu(ctx, ui);
|
||||
self.show_menu(ctx, ui, &mut toasts);
|
||||
});
|
||||
});
|
||||
if self.color_picker.is_some() {
|
||||
|
||||
Reference in New Issue
Block a user