2020-06-08 19:32:58 +00:00
|
|
|
#![windows_subsystem = "windows"]
|
2019-11-17 22:15:43 +00:00
|
|
|
#![allow(clippy::unneeded_field_pattern)]
|
|
|
|
|
2019-05-03 09:33:58 +00:00
|
|
|
mod audio;
|
2020-01-29 00:23:02 +00:00
|
|
|
mod custom_event;
|
|
|
|
mod executor;
|
2019-12-18 21:38:55 +00:00
|
|
|
mod input;
|
2019-09-02 21:23:26 +00:00
|
|
|
mod navigator;
|
2020-06-12 18:35:12 +00:00
|
|
|
mod storage;
|
2020-06-15 16:53:19 +00:00
|
|
|
mod task;
|
2019-05-03 02:11:47 +00:00
|
|
|
|
2020-01-29 00:23:02 +00:00
|
|
|
use crate::custom_event::RuffleEvent;
|
|
|
|
use crate::executor::GlutinAsyncExecutor;
|
2019-12-01 03:40:57 +00:00
|
|
|
use ruffle_core::{
|
|
|
|
backend::audio::{AudioBackend, NullAudioBackend},
|
|
|
|
Player,
|
|
|
|
};
|
2020-05-01 03:00:07 +00:00
|
|
|
use ruffle_render_wgpu::WgpuRenderBackend;
|
2019-04-28 06:08:59 +00:00
|
|
|
use std::path::PathBuf;
|
2019-10-29 03:35:26 +00:00
|
|
|
use std::time::Instant;
|
2019-04-28 06:08:59 +00:00
|
|
|
use structopt::StructOpt;
|
|
|
|
|
2020-06-15 16:53:19 +00:00
|
|
|
use crate::storage::DiskStorageBackend;
|
2020-05-04 19:37:06 +00:00
|
|
|
use ruffle_core::tag_utils::SwfMovie;
|
2020-04-21 13:32:50 +00:00
|
|
|
use std::rc::Rc;
|
|
|
|
use winit::dpi::{LogicalSize, PhysicalPosition};
|
|
|
|
use winit::event::{ElementState, MouseButton, WindowEvent};
|
|
|
|
use winit::event_loop::{ControlFlow, EventLoop};
|
2020-06-09 13:30:27 +00:00
|
|
|
use winit::window::{Icon, WindowBuilder};
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2019-04-28 06:08:59 +00:00
|
|
|
#[derive(StructOpt, Debug)]
|
|
|
|
#[structopt(name = "basic")]
|
|
|
|
struct Opt {
|
|
|
|
#[structopt(name = "FILE", parse(from_os_str))]
|
|
|
|
input_path: PathBuf,
|
|
|
|
}
|
2019-04-25 17:52:22 +00:00
|
|
|
|
2019-04-27 01:55:06 +00:00
|
|
|
fn main() {
|
2019-05-02 00:46:49 +00:00
|
|
|
env_logger::init();
|
|
|
|
|
2019-04-28 06:08:59 +00:00
|
|
|
let opt = Opt::from_args();
|
|
|
|
|
|
|
|
let ret = run_player(opt.input_path);
|
|
|
|
|
|
|
|
if let Err(e) = ret {
|
|
|
|
eprintln!("Fatal error:\n{}", e);
|
|
|
|
std::process::exit(-1);
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|
2019-04-28 06:08:59 +00:00
|
|
|
}
|
2019-04-25 17:52:22 +00:00
|
|
|
|
2019-08-15 20:48:51 +00:00
|
|
|
fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
2020-05-04 19:37:06 +00:00
|
|
|
let movie = SwfMovie::from_path(&input_path)?;
|
2020-05-12 06:56:55 +00:00
|
|
|
let movie_size = LogicalSize::new(movie.width(), movie.height());
|
2019-04-28 06:08:59 +00:00
|
|
|
|
2020-06-08 09:41:25 +00:00
|
|
|
let icon_bytes = include_bytes!("../assets/favicon-32.rgba");
|
|
|
|
let icon = Icon::from_rgba(icon_bytes.to_vec(), 32, 32)?;
|
|
|
|
|
2020-01-29 00:23:02 +00:00
|
|
|
let event_loop: EventLoop<RuffleEvent> = EventLoop::with_user_event();
|
2020-04-21 13:32:50 +00:00
|
|
|
let window = Rc::new(
|
|
|
|
WindowBuilder::new()
|
|
|
|
.with_title(format!(
|
|
|
|
"Ruffle - {}",
|
|
|
|
input_path.file_name().unwrap_or_default().to_string_lossy()
|
|
|
|
))
|
2020-06-08 09:41:25 +00:00
|
|
|
.with_window_icon(Some(icon))
|
2020-05-12 06:56:55 +00:00
|
|
|
.with_inner_size(movie_size)
|
2020-04-21 13:32:50 +00:00
|
|
|
.build(&event_loop)?,
|
|
|
|
);
|
2020-05-12 06:56:55 +00:00
|
|
|
let viewport_size = movie_size.to_physical(window.scale_factor());
|
2020-04-21 13:32:50 +00:00
|
|
|
|
2019-12-01 03:40:57 +00:00
|
|
|
let audio: Box<dyn AudioBackend> = match audio::CpalAudioBackend::new() {
|
|
|
|
Ok(audio) => Box::new(audio),
|
|
|
|
Err(e) => {
|
|
|
|
log::error!("Unable to create audio device: {}", e);
|
|
|
|
Box::new(NullAudioBackend::new())
|
|
|
|
}
|
|
|
|
};
|
2020-05-04 20:59:06 +00:00
|
|
|
let renderer = Box::new(WgpuRenderBackend::for_window(
|
2020-04-27 15:53:42 +00:00
|
|
|
window.as_ref(),
|
2020-05-12 06:56:55 +00:00
|
|
|
(viewport_size.width, viewport_size.height),
|
2020-04-27 15:53:42 +00:00
|
|
|
)?);
|
2020-01-29 00:23:02 +00:00
|
|
|
let (executor, chan) = GlutinAsyncExecutor::new(event_loop.create_proxy());
|
2020-02-24 09:49:28 +00:00
|
|
|
let navigator = Box::new(navigator::ExternalNavigatorBackend::with_base_path(
|
|
|
|
input_path
|
|
|
|
.parent()
|
|
|
|
.unwrap_or_else(|| std::path::Path::new("")),
|
2020-01-29 00:23:02 +00:00
|
|
|
chan,
|
|
|
|
event_loop.create_proxy(),
|
|
|
|
)); //TODO: actually implement this backend type
|
2020-04-21 13:32:50 +00:00
|
|
|
let input = Box::new(input::WinitInputBackend::new(window.clone()));
|
2020-06-12 18:35:12 +00:00
|
|
|
let storage = Box::new(DiskStorageBackend::new());
|
|
|
|
let player = Player::new(renderer, audio, navigator, input, movie, storage)?;
|
2020-05-04 19:51:51 +00:00
|
|
|
player.lock().unwrap().set_is_playing(true); // Desktop player will auto-play.
|
2019-04-26 03:27:44 +00:00
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
player
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2020-05-12 06:56:55 +00:00
|
|
|
.set_viewport_dimensions(viewport_size.width, viewport_size.height);
|
2019-05-01 00:46:32 +00:00
|
|
|
|
2019-08-20 05:23:02 +00:00
|
|
|
let mut mouse_pos = PhysicalPosition::new(0.0, 0.0);
|
2019-04-28 06:08:59 +00:00
|
|
|
let mut time = Instant::now();
|
2020-05-02 11:25:21 +00:00
|
|
|
let mut next_frame_time = Instant::now();
|
2019-04-28 06:08:59 +00:00
|
|
|
loop {
|
|
|
|
// Poll UI events
|
2019-11-11 16:29:33 +00:00
|
|
|
event_loop.run(move |event, _window_target, control_flow| {
|
|
|
|
match event {
|
2020-04-21 13:32:50 +00:00
|
|
|
winit::event::Event::LoopDestroyed => return,
|
2020-05-02 11:25:21 +00:00
|
|
|
|
|
|
|
// Core loop
|
|
|
|
winit::event::Event::MainEventsCleared => {
|
|
|
|
let new_time = Instant::now();
|
|
|
|
let dt = new_time.duration_since(time).as_micros();
|
|
|
|
if dt > 0 {
|
|
|
|
time = new_time;
|
|
|
|
let mut player_lock = player.lock().unwrap();
|
|
|
|
player_lock.tick(dt as f64 / 1000.0);
|
|
|
|
next_frame_time = new_time + player_lock.time_til_next_frame();
|
|
|
|
if player_lock.needs_render() {
|
|
|
|
window.request_redraw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Render
|
|
|
|
winit::event::Event::RedrawRequested(_) => player.lock().unwrap().render(),
|
|
|
|
|
2020-04-21 13:32:50 +00:00
|
|
|
winit::event::Event::WindowEvent { event, .. } => match event {
|
2020-02-14 20:11:12 +00:00
|
|
|
WindowEvent::Resized(size) => {
|
2019-11-08 20:09:57 +00:00
|
|
|
let mut player_lock = player.lock().unwrap();
|
|
|
|
player_lock.set_viewport_dimensions(size.width, size.height);
|
|
|
|
player_lock
|
2020-02-14 20:11:12 +00:00
|
|
|
.renderer_mut()
|
|
|
|
.set_viewport_dimensions(size.width, size.height);
|
2020-05-02 11:25:21 +00:00
|
|
|
window.request_redraw();
|
2019-08-10 17:18:32 +00:00
|
|
|
}
|
2019-05-03 18:44:12 +00:00
|
|
|
WindowEvent::CursorMoved { position, .. } => {
|
2019-11-08 20:09:57 +00:00
|
|
|
let mut player_lock = player.lock().unwrap();
|
2019-05-03 18:44:12 +00:00
|
|
|
mouse_pos = position;
|
2019-08-19 14:53:12 +00:00
|
|
|
let event = ruffle_core::PlayerEvent::MouseMove {
|
2019-08-20 05:23:02 +00:00
|
|
|
x: position.x,
|
|
|
|
y: position.y,
|
2019-08-17 01:04:26 +00:00
|
|
|
};
|
2019-11-08 20:09:57 +00:00
|
|
|
player_lock.handle_event(event);
|
2020-05-02 11:25:21 +00:00
|
|
|
if player_lock.needs_render() {
|
|
|
|
window.request_redraw();
|
|
|
|
}
|
2019-05-03 18:44:12 +00:00
|
|
|
}
|
|
|
|
WindowEvent::MouseInput {
|
|
|
|
button: MouseButton::Left,
|
2019-08-17 01:04:26 +00:00
|
|
|
state: pressed,
|
2019-05-03 18:44:12 +00:00
|
|
|
..
|
2019-08-14 19:39:26 +00:00
|
|
|
} => {
|
2019-11-08 20:09:57 +00:00
|
|
|
let mut player_lock = player.lock().unwrap();
|
2019-08-17 01:04:26 +00:00
|
|
|
let event = if pressed == ElementState::Pressed {
|
2019-08-19 14:53:12 +00:00
|
|
|
ruffle_core::PlayerEvent::MouseDown {
|
2019-08-20 05:23:02 +00:00
|
|
|
x: mouse_pos.x,
|
|
|
|
y: mouse_pos.y,
|
2019-08-17 01:04:26 +00:00
|
|
|
}
|
|
|
|
} else {
|
2019-08-19 14:53:12 +00:00
|
|
|
ruffle_core::PlayerEvent::MouseUp {
|
2019-08-20 05:23:02 +00:00
|
|
|
x: mouse_pos.x,
|
|
|
|
y: mouse_pos.y,
|
2019-08-17 01:04:26 +00:00
|
|
|
}
|
|
|
|
};
|
2019-11-08 20:09:57 +00:00
|
|
|
player_lock.handle_event(event);
|
2020-05-02 11:25:21 +00:00
|
|
|
if player_lock.needs_render() {
|
|
|
|
window.request_redraw();
|
|
|
|
}
|
2019-08-17 01:04:26 +00:00
|
|
|
}
|
|
|
|
WindowEvent::CursorLeft { .. } => {
|
2019-11-08 20:09:57 +00:00
|
|
|
let mut player_lock = player.lock().unwrap();
|
2020-05-02 11:25:21 +00:00
|
|
|
player_lock.handle_event(ruffle_core::PlayerEvent::MouseLeft);
|
|
|
|
if player_lock.needs_render() {
|
|
|
|
window.request_redraw();
|
|
|
|
}
|
2019-08-14 19:39:26 +00:00
|
|
|
}
|
2019-11-11 16:29:33 +00:00
|
|
|
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
2019-12-24 10:41:35 +00:00
|
|
|
WindowEvent::KeyboardInput { .. } | WindowEvent::ReceivedCharacter(_) => {
|
2019-11-08 20:09:57 +00:00
|
|
|
let mut player_lock = player.lock().unwrap();
|
|
|
|
if let Some(event) = player_lock
|
|
|
|
.input_mut()
|
|
|
|
.downcast_mut::<input::WinitInputBackend>()
|
|
|
|
.unwrap()
|
|
|
|
.handle_event(event)
|
|
|
|
{
|
|
|
|
player_lock.handle_event(event);
|
2020-05-02 11:25:21 +00:00
|
|
|
if player_lock.needs_render() {
|
|
|
|
window.request_redraw();
|
|
|
|
}
|
2019-12-24 10:41:35 +00:00
|
|
|
}
|
2019-12-18 21:38:55 +00:00
|
|
|
}
|
2019-05-03 18:44:12 +00:00
|
|
|
_ => (),
|
2019-11-11 16:29:33 +00:00
|
|
|
},
|
2020-04-21 13:32:50 +00:00
|
|
|
winit::event::Event::UserEvent(RuffleEvent::TaskPoll) => executor
|
2020-01-29 00:23:02 +00:00
|
|
|
.lock()
|
|
|
|
.expect("active executor reference")
|
|
|
|
.poll_all(),
|
2019-11-11 16:29:33 +00:00
|
|
|
_ => (),
|
2019-05-03 18:44:12 +00:00
|
|
|
}
|
2019-04-28 06:08:59 +00:00
|
|
|
|
2019-11-18 21:23:16 +00:00
|
|
|
// After polling events, sleep the event loop until the next event or the next frame.
|
2020-05-02 11:25:21 +00:00
|
|
|
if *control_flow != ControlFlow::Exit {
|
|
|
|
*control_flow = ControlFlow::WaitUntil(next_frame_time);
|
2019-11-18 21:23:16 +00:00
|
|
|
}
|
2019-11-11 16:29:33 +00:00
|
|
|
});
|
2019-04-28 06:08:59 +00:00
|
|
|
}
|
2019-04-25 17:52:22 +00:00
|
|
|
}
|