desktop: Recreate Player for each movie, and don't start with a Player unless a movie has been specified

This commit is contained in:
Nathan Adams 2023-05-21 22:23:43 +02:00
parent df51531de6
commit e97d32dbc7
2 changed files with 103 additions and 70 deletions

View File

@ -6,7 +6,6 @@ use crate::util::{
get_screen_size, parse_url, pick_file, winit_key_to_char, winit_to_ruffle_key_code, get_screen_size, parse_url, pick_file, winit_key_to_char, winit_to_ruffle_key_code,
winit_to_ruffle_text_control, winit_to_ruffle_text_control,
}; };
use crate::{CALLSTACK, SWF_INFO};
use anyhow::{Context, Error}; use anyhow::{Context, Error};
use ruffle_core::{PlayerEvent, StageDisplayState}; use ruffle_core::{PlayerEvent, StageDisplayState};
use ruffle_render::backend::ViewportDimensions; use ruffle_render::backend::ViewportDimensions;
@ -14,17 +13,15 @@ use ruffle_render_wgpu::backend::WgpuRenderBackend;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Instant; use std::time::Instant;
use url::Url;
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Size}; use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Size};
use winit::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}; use winit::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy}; use winit::event_loop::{ControlFlow, EventLoop, EventLoopBuilder};
use winit::window::{Fullscreen, Icon, Window, WindowBuilder}; use winit::window::{Fullscreen, Icon, Window, WindowBuilder};
pub struct App { pub struct App {
opt: Opt, opt: Opt,
window: Rc<Window>, window: Rc<Window>,
event_loop: Option<EventLoop<RuffleEvent>>, event_loop: Option<EventLoop<RuffleEvent>>,
event_loop_proxy: EventLoopProxy<RuffleEvent>,
gui: Arc<Mutex<GuiController>>, gui: Arc<Mutex<GuiController>>,
player: PlayerController, player: PlayerController,
min_window_size: LogicalSize<u32>, min_window_size: LogicalSize<u32>,
@ -65,61 +62,25 @@ impl App {
opt.power.into(), opt.power.into(),
)?; )?;
let player = PlayerController::new( let mut player = PlayerController::new(
&opt,
event_loop.create_proxy(), event_loop.create_proxy(),
movie_url.clone(),
window.clone(), window.clone(),
gui.descriptors().clone(), gui.descriptors().clone(),
gui.create_movie_view(),
); );
let mut app = Self { if let Some(movie_url) = movie_url {
player.create(&opt, movie_url, gui.create_movie_view());
}
Ok(Self {
opt, opt,
window, window,
event_loop_proxy: event_loop.create_proxy(),
event_loop: Some(event_loop), event_loop: Some(event_loop),
gui: Arc::new(Mutex::new(gui)), gui: Arc::new(Mutex::new(gui)),
player, player,
min_window_size, min_window_size,
max_window_size, max_window_size,
}; })
if let Some(movie_url) = movie_url {
app.load_swf(movie_url)?;
}
Ok(app)
}
fn load_swf(&mut self, url: Url) -> Result<(), Error> {
let filename = url
.path_segments()
.and_then(|segments| segments.last())
.unwrap_or_else(|| url.as_str());
let title = format!("Ruffle - {filename}");
self.window.set_title(&title);
SWF_INFO.with(|i| *i.borrow_mut() = Some(filename.to_string()));
let event_loop_proxy = self.event_loop_proxy.clone();
let on_metadata = move |swf_header: &ruffle_core::swf::HeaderExt| {
let _ = event_loop_proxy.send_event(RuffleEvent::OnMetadata(swf_header.clone()));
};
let mut parameters: Vec<(String, String)> = url.query_pairs().into_owned().collect();
parameters.extend(self.opt.parameters());
if let Some(mut player) = self.player.get() {
CALLSTACK.with(|callstack| {
*callstack.borrow_mut() = Some(player.callstack());
});
player.fetch_root_movie(url.to_string(), parameters, Box::new(on_metadata));
} else {
unimplemented!("TODO: get_or_create player");
}
Ok(())
} }
pub fn run(mut self) -> ! { pub fn run(mut self) -> ! {
@ -457,13 +418,21 @@ impl App {
if let Some(path) = pick_file() { if let Some(path) = pick_file() {
// TODO: Show dialog on error. // TODO: Show dialog on error.
let url = parse_url(&path).expect("Couldn't load specified path"); let url = parse_url(&path).expect("Couldn't load specified path");
let _ = self.load_swf(url); self.player.create(
&self.opt,
url,
self.gui.lock().expect("Gui lock").create_movie_view(),
);
self.gui.lock().expect("Gui lock").set_ui_visible(false); self.gui.lock().expect("Gui lock").set_ui_visible(false);
} }
} }
winit::event::Event::UserEvent(RuffleEvent::OpenURL(url)) => { winit::event::Event::UserEvent(RuffleEvent::OpenURL(url)) => {
let _ = self.load_swf(url); self.player.create(
&self.opt,
url,
self.gui.lock().expect("Gui lock").create_movie_view(),
);
self.gui.lock().expect("Gui lock").set_ui_visible(false); self.gui.lock().expect("Gui lock").set_ui_visible(false);
} }

View File

@ -2,7 +2,7 @@ use crate::cli::Opt;
use crate::custom_event::RuffleEvent; use crate::custom_event::RuffleEvent;
use crate::executor::GlutinAsyncExecutor; use crate::executor::GlutinAsyncExecutor;
use crate::gui::MovieView; use crate::gui::MovieView;
use crate::RENDER_INFO; use crate::{CALLSTACK, RENDER_INFO, SWF_INFO};
use anyhow::anyhow; use anyhow::anyhow;
use ruffle_core::backend::audio::AudioBackend; use ruffle_core::backend::audio::AudioBackend;
use ruffle_core::{Player, PlayerBuilder}; use ruffle_core::{Player, PlayerBuilder};
@ -16,16 +16,16 @@ use url::Url;
use winit::event_loop::EventLoopProxy; use winit::event_loop::EventLoopProxy;
use winit::window::Window; use winit::window::Window;
pub struct PlayerController { struct ActivePlayer {
player: Arc<Mutex<Player>>, player: Arc<Mutex<Player>>,
executor: Arc<Mutex<GlutinAsyncExecutor>>, executor: Arc<Mutex<GlutinAsyncExecutor>>,
} }
impl PlayerController { impl ActivePlayer {
pub fn new( pub fn new(
opt: &Opt, opt: &Opt,
event_loop: EventLoopProxy<RuffleEvent>, event_loop: EventLoopProxy<RuffleEvent>,
movie_url: Option<Url>, movie_url: Url,
window: Rc<Window>, window: Rc<Window>,
descriptors: Arc<Descriptors>, descriptors: Arc<Descriptors>,
movie_view: MovieView, movie_view: MovieView,
@ -44,11 +44,9 @@ impl PlayerController {
let (executor, channel) = GlutinAsyncExecutor::new(event_loop.clone()); let (executor, channel) = GlutinAsyncExecutor::new(event_loop.clone());
let navigator = crate::navigator::ExternalNavigatorBackend::new( let navigator = crate::navigator::ExternalNavigatorBackend::new(
opt.base.to_owned().unwrap_or( opt.base.to_owned().unwrap_or_else(|| movie_url.clone()),
movie_url.unwrap_or_else(|| Url::parse("file:///empty").expect("Dummy Url")),
),
channel, channel,
event_loop, event_loop.clone(),
opt.proxy.clone(), opt.proxy.clone(),
opt.upgrade_to_https, opt.upgrade_to_https,
opt.open_url_mode, opt.open_url_mode,
@ -70,7 +68,10 @@ impl PlayerController {
.with_storage( .with_storage(
crate::storage::DiskStorageBackend::new().expect("Couldn't create storage backend"), crate::storage::DiskStorageBackend::new().expect("Couldn't create storage backend"),
) )
.with_ui(crate::ui::DesktopUiBackend::new(window).expect("Couldn't create ui backend")) .with_ui(
crate::ui::DesktopUiBackend::new(window.clone())
.expect("Couldn't create ui backend"),
)
.with_autoplay(true) .with_autoplay(true)
.with_letterbox(opt.letterbox) .with_letterbox(opt.letterbox)
.with_max_execution_duration(Duration::from_secs_f64(opt.max_execution_duration)) .with_max_execution_duration(Duration::from_secs_f64(opt.max_execution_duration))
@ -82,26 +83,89 @@ impl PlayerController {
.with_spoofed_url(opt.spoof_url.clone().map(|url| url.to_string())) .with_spoofed_url(opt.spoof_url.clone().map(|url| url.to_string()))
.with_player_version(opt.player_version) .with_player_version(opt.player_version)
.with_frame_rate(opt.frame_rate); .with_frame_rate(opt.frame_rate);
let player = builder.build();
let name = movie_url
.path_segments()
.and_then(|segments| segments.last())
.unwrap_or_else(|| movie_url.as_str())
.to_string();
window.set_title(&format!("Ruffle - {name}"));
SWF_INFO.with(|i| *i.borrow_mut() = Some(name.clone()));
let on_metadata = move |swf_header: &ruffle_core::swf::HeaderExt| {
let _ = event_loop.send_event(RuffleEvent::OnMetadata(swf_header.clone()));
};
let mut parameters: Vec<(String, String)> = movie_url.query_pairs().into_owned().collect();
parameters.extend(opt.parameters());
{
let mut player_lock = player.lock().expect("Player lock must be available");
CALLSTACK.with(|callstack| {
*callstack.borrow_mut() = Some(player_lock.callstack());
});
player_lock.fetch_root_movie(movie_url.to_string(), parameters, Box::new(on_metadata));
}
Self { player, executor }
}
}
pub struct PlayerController {
player: Option<ActivePlayer>,
event_loop: EventLoopProxy<RuffleEvent>,
window: Rc<Window>,
descriptors: Arc<Descriptors>,
}
impl PlayerController {
pub fn new(
event_loop: EventLoopProxy<RuffleEvent>,
window: Rc<Window>,
descriptors: Arc<Descriptors>,
) -> Self {
Self { Self {
player: builder.build(), player: None,
executor, event_loop,
window,
descriptors,
} }
} }
pub fn create(&mut self, opt: &Opt, movie_url: Url, movie_view: MovieView) {
self.player = Some(ActivePlayer::new(
opt,
self.event_loop.clone(),
movie_url,
self.window.clone(),
self.descriptors.clone(),
movie_view,
));
}
pub fn get(&self) -> Option<MutexGuard<Player>> { pub fn get(&self) -> Option<MutexGuard<Player>> {
// We don't want to return None when the lock fails to grab as that's a fatal error, not a lack of player match &self.player {
Some( None => None,
self.player // We don't want to return None when the lock fails to grab as that's a fatal error, not a lack of player
.try_lock() Some(player) => Some(
.expect("Player lock must be available"), player
) .player
.try_lock()
.expect("Player lock must be available"),
),
}
} }
pub fn poll(&self) { pub fn poll(&self) {
self.executor if let Some(player) = &self.player {
.lock() player
.expect("active executor reference") .executor
.poll_all() .lock()
.expect("Executor lock must be available")
.poll_all()
}
} }
} }