frontend-utils: Extracted out a PlayerOptions struct from desktop

This commit is contained in:
Nathan Adams 2024-04-25 00:14:11 +02:00
parent f748dd747d
commit 313ee5dd27
6 changed files with 156 additions and 105 deletions

1
Cargo.lock generated
View File

@ -4386,6 +4386,7 @@ dependencies = [
"macro_rules_attribute",
"reqwest",
"ruffle_core",
"ruffle_render",
"slotmap",
"tempfile",
"thiserror",

View File

@ -50,11 +50,11 @@ impl OpenDialog {
event_loop: EventLoopProxy<RuffleEvent>,
) -> Self {
let spoof_url = OptionalField::new(
defaults.spoof_url.as_ref().map(Url::to_string),
defaults.player.spoof_url.as_ref().map(Url::to_string),
UrlField::new("https://example.org/game.swf"),
);
let base_url = OptionalField::new(
defaults.base.as_ref().map(Url::to_string),
defaults.player.base.as_ref().map(Url::to_string),
UrlField::new("https://example.org"),
);
let proxy_url = OptionalField::new(
@ -64,6 +64,7 @@ impl OpenDialog {
let path = PathOrUrlField::new(default_url, "path/to/movie.swf");
let script_timeout = OptionalField::new(
defaults
.player
.max_execution_duration
.as_ref()
.map(Duration::as_secs_f64),
@ -82,7 +83,7 @@ impl OpenDialog {
),
);
let quality = OptionalField::new(
defaults.quality,
defaults.player.quality,
EnumDropdownField::new(
StageQuality::High,
vec![
@ -109,8 +110,9 @@ impl OpenDialog {
);
let align = OptionalField::new(
defaults
.player
.align
.map(|a| (a, defaults.force_align.unwrap_or_default())),
.map(|a| (a, defaults.player.force_align.unwrap_or_default())),
FieldWithCheckbox::new(
EnumDropdownField::new(
StageAlign::default(),
@ -151,8 +153,9 @@ impl OpenDialog {
);
let scale_mode = OptionalField::new(
defaults
.player
.scale
.map(|a| (a, defaults.force_scale.unwrap_or_default())),
.map(|a| (a, defaults.player.force_scale.unwrap_or_default())),
FieldWithCheckbox::new(
EnumDropdownField::new(
StageScaleMode::default(),
@ -174,7 +177,7 @@ impl OpenDialog {
),
);
let load_behavior = OptionalField::new(
defaults.load_behavior,
defaults.player.load_behavior,
EnumDropdownField::new(
LoadBehavior::Streaming,
vec![
@ -190,7 +193,7 @@ impl OpenDialog {
),
);
let letterbox = OptionalField::new(
defaults.letterbox,
defaults.player.letterbox,
EnumDropdownField::new(
Letterbox::On,
vec![Letterbox::On, Letterbox::Fullscreen, Letterbox::Off],
@ -202,9 +205,9 @@ impl OpenDialog {
),
);
let player_version =
OptionalField::new(defaults.player_version, NumberField::new(1..=32, 32));
OptionalField::new(defaults.player.player_version, NumberField::new(1..=32, 32));
let player_runtime = OptionalField::new(
defaults.player_runtime,
defaults.player.player_runtime,
EnumDropdownField::new(
PlayerRuntime::default(),
vec![PlayerRuntime::FlashPlayer, PlayerRuntime::AIR],
@ -215,7 +218,7 @@ impl OpenDialog {
),
);
let dummy_external_interface = OptionalField::new(
defaults.dummy_external_interface,
defaults.player.dummy_external_interface,
BooleanDropdownField::new(
false,
Box::new(|value, locale| match value {
@ -225,7 +228,7 @@ impl OpenDialog {
),
);
let upgrade_to_https = OptionalField::new(
defaults.upgrade_to_https,
defaults.player.upgrade_to_https,
BooleanDropdownField::new(
false,
Box::new(|value, locale| match value {
@ -260,9 +263,9 @@ impl OpenDialog {
fn start(&mut self) -> bool {
if self.framerate_enabled {
self.options.frame_rate = Some(self.framerate);
self.options.player.frame_rate = Some(self.framerate);
} else {
self.options.frame_rate = None;
self.options.player.frame_rate = None;
}
if let Some(url) = self.path.value() {
if self
@ -340,14 +343,14 @@ impl OpenDialog {
ui.label(text(locale, "custom-base-url"));
is_valid &= self
.base_url
.ui(ui, &mut self.options.base, locale)
.ui(ui, &mut self.options.player.base, locale)
.is_valid();
ui.end_row();
ui.label(text(locale, "spoof-swf-url"));
is_valid &= self
.spoof_url
.ui(ui, &mut self.options.spoof_url, locale)
.ui(ui, &mut self.options.player.spoof_url, locale)
.is_valid();
ui.end_row();
@ -360,7 +363,7 @@ impl OpenDialog {
ui.label(text(locale, "upgrade-http"));
self.upgrade_to_https
.ui(ui, &mut self.options.upgrade_to_https, locale);
.ui(ui, &mut self.options.player.upgrade_to_https, locale);
ui.end_row();
ui.label(text(locale, "tcp-connections"));
@ -397,7 +400,7 @@ impl OpenDialog {
ui.label(text(locale, "load-behavior"));
self.load_behavior
.ui(ui, &mut self.options.load_behavior, locale);
.ui(ui, &mut self.options.player.load_behavior, locale);
ui.end_row();
});
@ -412,31 +415,34 @@ impl OpenDialog {
.show(ui, |ui| {
ui.label(text(locale, "max-execution-duration"));
self.script_timeout
.ui(ui, &mut self.options.max_execution_duration, locale);
.ui(ui, &mut self.options.player.max_execution_duration, locale);
ui.end_row();
ui.label(text(locale, "quality"));
self.quality.ui(ui, &mut self.options.quality, locale);
self.quality
.ui(ui, &mut self.options.player.quality, locale);
ui.end_row();
ui.label(text(locale, "letterbox"));
self.letterbox.ui(ui, &mut self.options.letterbox, locale);
self.letterbox
.ui(ui, &mut self.options.player.letterbox, locale);
ui.end_row();
ui.label(text(locale, "align"));
let mut align = self
.options
.player
.align
.map(|a| (a, self.options.force_align.unwrap_or_default()));
.map(|a| (a, self.options.player.force_align.unwrap_or_default()));
self.align.ui(ui, &mut align, locale);
match align {
Some((align, force)) => {
self.options.align = Some(align);
self.options.force_align = Some(force);
self.options.player.align = Some(align);
self.options.player.force_align = Some(force);
}
None => {
self.options.align = None;
self.options.force_align = None;
self.options.player.align = None;
self.options.player.force_align = None;
}
}
ui.end_row();
@ -444,17 +450,18 @@ impl OpenDialog {
ui.label(text(locale, "scale-mode"));
let mut scale_mode = self
.options
.player
.scale
.map(|a| (a, self.options.force_scale.unwrap_or_default()));
.map(|a| (a, self.options.player.force_scale.unwrap_or_default()));
self.scale_mode.ui(ui, &mut scale_mode, locale);
match scale_mode {
Some((scale, force)) => {
self.options.scale = Some(scale);
self.options.force_scale = Some(force);
self.options.player.scale = Some(scale);
self.options.player.force_scale = Some(force);
}
None => {
self.options.scale = None;
self.options.force_scale = None;
self.options.player.scale = None;
self.options.player.force_scale = None;
}
}
ui.end_row();
@ -462,19 +469,19 @@ impl OpenDialog {
ui.label(text(locale, "dummy-external-interface"));
self.dummy_external_interface.ui(
ui,
&mut self.options.dummy_external_interface,
&mut self.options.player.dummy_external_interface,
locale,
);
ui.end_row();
ui.label(text(locale, "player-version"));
self.player_version
.ui(ui, &mut self.options.player_version, locale);
.ui(ui, &mut self.options.player.player_version, locale);
ui.end_row();
ui.label(text(locale, "player-runtime"));
self.player_runtime
.ui(ui, &mut self.options.player_runtime, locale);
.ui(ui, &mut self.options.player.player_runtime, locale);
ui.end_row();
ui.label(text(locale, "custom-framerate"));
@ -500,18 +507,19 @@ impl OpenDialog {
.clicked()
{
self.options
.player
.parameters
.push((Default::default(), Default::default()));
}
if ui
.add_enabled(
!self.options.parameters.is_empty(),
!self.options.player.parameters.is_empty(),
Button::new(text(locale, "open-dialog-remove-parameters")),
)
.clicked()
{
self.options.parameters.clear();
self.options.player.parameters.clear();
}
});
@ -521,7 +529,7 @@ impl OpenDialog {
.min_col_width(100.0)
.striped(true)
.show(ui, |ui| {
self.options.parameters.retain_mut(|(key, value)| {
self.options.player.parameters.retain_mut(|(key, value)| {
let mut keep = true;
ui.text_edit_singleline(key);
ui.horizontal(|ui| {

View File

@ -10,12 +10,10 @@ use anyhow::anyhow;
use ruffle_core::backend::navigator::{OpenURLMode, SocketMode};
use ruffle_core::config::Letterbox;
use ruffle_core::events::{GamepadButton, KeyCode};
use ruffle_core::{
DefaultFont, LoadBehavior, Player, PlayerBuilder, PlayerEvent, PlayerRuntime, StageAlign,
StageScaleMode,
};
use ruffle_core::{DefaultFont, LoadBehavior, Player, PlayerBuilder, PlayerEvent};
use ruffle_frontend_utils::backends::executor::{AsyncExecutor, PollRequester};
use ruffle_frontend_utils::backends::navigator::ExternalNavigatorBackend;
use ruffle_frontend_utils::bundle::player_options::PlayerOptions;
use ruffle_frontend_utils::bundle::source::BundleSourceError;
use ruffle_frontend_utils::bundle::{Bundle, BundleError};
use ruffle_frontend_utils::content::PlayingContent;
@ -37,28 +35,13 @@ use winit::window::Window;
/// These may be primed by command line arguments.
#[derive(Debug, Clone)]
pub struct LaunchOptions {
pub parameters: Vec<(String, String)>,
pub max_execution_duration: Option<Duration>,
pub base: Option<Url>,
pub quality: Option<StageQuality>,
pub align: Option<StageAlign>,
pub force_align: Option<bool>,
pub scale: Option<StageScaleMode>,
pub force_scale: Option<bool>,
pub player: PlayerOptions,
pub proxy: Option<Url>,
pub socket_allowed: HashSet<String>,
pub tcp_connections: Option<SocketMode>,
pub upgrade_to_https: Option<bool>,
pub fullscreen: bool,
pub load_behavior: Option<LoadBehavior>,
pub save_directory: PathBuf,
pub letterbox: Option<Letterbox>,
pub spoof_url: Option<Url>,
pub player_version: Option<u8>,
pub player_runtime: Option<PlayerRuntime>,
pub frame_rate: Option<f64>,
pub open_url_mode: OpenURLMode,
pub dummy_external_interface: Option<bool>,
pub gamepad_button_mapping: HashMap<GamepadButton, KeyCode>,
pub avm2_optimizer_enabled: bool,
}
@ -66,42 +49,44 @@ pub struct LaunchOptions {
impl From<&GlobalPreferences> for LaunchOptions {
fn from(value: &GlobalPreferences) -> Self {
Self {
parameters: value.cli.parameters().collect(),
max_execution_duration: value.cli.max_execution_duration,
base: value.cli.base.clone(),
quality: value.cli.quality,
align: value.cli.align,
force_align: if value.cli.force_align {
Some(true)
} else {
None
},
scale: value.cli.scale,
force_scale: if value.cli.force_scale {
Some(true)
} else {
None
player: PlayerOptions {
parameters: value.cli.parameters().collect(),
max_execution_duration: value.cli.max_execution_duration,
base: value.cli.base.clone(),
quality: value.cli.quality,
align: value.cli.align,
force_align: if value.cli.force_align {
Some(true)
} else {
None
},
scale: value.cli.scale,
force_scale: if value.cli.force_scale {
Some(true)
} else {
None
},
upgrade_to_https: if value.cli.upgrade_to_https {
Some(true)
} else {
None
},
load_behavior: value.cli.load_behavior,
letterbox: value.cli.letterbox,
spoof_url: value.cli.spoof_url.clone(),
player_version: value.cli.player_version,
player_runtime: value.cli.player_runtime,
frame_rate: value.cli.frame_rate,
dummy_external_interface: if value.cli.dummy_external_interface {
Some(true)
} else {
None
},
},
proxy: value.cli.proxy.clone(),
upgrade_to_https: if value.cli.upgrade_to_https {
Some(true)
} else {
None
},
fullscreen: value.cli.fullscreen,
load_behavior: value.cli.load_behavior,
save_directory: value.cli.save_directory.clone(),
letterbox: value.cli.letterbox,
spoof_url: value.cli.spoof_url.clone(),
player_version: value.cli.player_version,
player_runtime: value.cli.player_runtime,
frame_rate: value.cli.frame_rate,
open_url_mode: value.cli.open_url_mode,
dummy_external_interface: if value.cli.dummy_external_interface {
Some(true)
} else {
None
},
socket_allowed: HashSet::from_iter(value.cli.socket_allow.iter().cloned()),
tcp_connections: value.cli.tcp_connections,
gamepad_button_mapping: HashMap::from_iter(value.cli.gamepad_button.iter().cloned()),
@ -183,10 +168,13 @@ impl ActivePlayer {
let movie_url = content.initial_swf_url().clone();
let readable_name = content.name();
let navigator = ExternalNavigatorBackend::new(
opt.base.to_owned().unwrap_or_else(|| movie_url.clone()),
opt.player
.base
.to_owned()
.unwrap_or_else(|| movie_url.clone()),
future_spawner,
opt.proxy.clone(),
opt.upgrade_to_https.unwrap_or_default(),
opt.player.upgrade_to_https.unwrap_or_default(),
opt.open_url_mode,
opt.socket_allowed.clone(),
opt.tcp_connections.unwrap_or(SocketMode::Ask),
@ -204,9 +192,9 @@ impl ActivePlayer {
.expect("Couldn't create wgpu rendering backend");
RENDER_INFO.with(|i| *i.borrow_mut() = Some(renderer.debug_info().to_string()));
if opt.dummy_external_interface.unwrap_or_default() {
if opt.player.dummy_external_interface.unwrap_or_default() {
builder = builder.with_external_interface(Box::new(DesktopExternalInterfaceProvider {
spoof_url: opt.spoof_url.clone(),
spoof_url: opt.player.spoof_url.clone(),
}));
}
@ -232,24 +220,24 @@ impl ActivePlayer {
.expect("Couldn't create ui backend"),
)
.with_autoplay(true)
.with_letterbox(opt.letterbox.unwrap_or(Letterbox::On))
.with_max_execution_duration(opt.max_execution_duration.unwrap_or(Duration::MAX))
.with_quality(opt.quality.unwrap_or(StageQuality::High))
.with_letterbox(opt.player.letterbox.unwrap_or(Letterbox::On))
.with_max_execution_duration(opt.player.max_execution_duration.unwrap_or(Duration::MAX))
.with_quality(opt.player.quality.unwrap_or(StageQuality::High))
.with_align(
opt.align.unwrap_or_default(),
opt.force_align.unwrap_or_default(),
opt.player.align.unwrap_or_default(),
opt.player.force_align.unwrap_or_default(),
)
.with_scale_mode(
opt.scale.unwrap_or_default(),
opt.force_scale.unwrap_or_default(),
opt.player.scale.unwrap_or_default(),
opt.player.force_scale.unwrap_or_default(),
)
.with_fullscreen(opt.fullscreen)
.with_load_behavior(opt.load_behavior.unwrap_or(LoadBehavior::Streaming))
.with_spoofed_url(opt.spoof_url.clone().map(|url| url.to_string()))
.with_page_url(opt.spoof_url.clone().map(|url| url.to_string()))
.with_player_version(opt.player_version)
.with_player_runtime(opt.player_runtime.unwrap_or_default())
.with_frame_rate(opt.frame_rate)
.with_load_behavior(opt.player.load_behavior.unwrap_or(LoadBehavior::Streaming))
.with_spoofed_url(opt.player.spoof_url.clone().map(|url| url.to_string()))
.with_page_url(opt.player.spoof_url.clone().map(|url| url.to_string()))
.with_player_version(opt.player.player_version)
.with_player_runtime(opt.player.player_runtime.unwrap_or_default())
.with_frame_rate(opt.player.frame_rate)
.with_avm2_optimizer_enabled(opt.avm2_optimizer_enabled);
let player = builder.build();
@ -268,7 +256,7 @@ impl ActivePlayer {
});
player_lock.fetch_root_movie(
movie_url.to_string(),
opt.parameters.to_owned(),
opt.player.parameters.to_owned(),
Box::new(on_metadata),
);

View File

@ -18,6 +18,7 @@ thiserror = { workspace = true }
zip = { version = "1.2.0", default-features = false, features = ["deflate"]}
urlencoding = "2.1.3"
ruffle_core = { path = "../core", default-features = false }
ruffle_render = { path = "../render", default-features = false }
async-channel = { workspace = true }
slotmap = { workspace = true }
futures = { workspace = true }

View File

@ -6,6 +6,7 @@ use crate::parse::ParseWarning;
use std::path::Path;
pub mod info;
pub mod player_options;
pub mod source;
#[derive(Debug, thiserror::Error)]

View File

@ -0,0 +1,52 @@
use ruffle_core::config::Letterbox;
use ruffle_core::{LoadBehavior, PlayerRuntime, StageAlign, StageScaleMode};
use ruffle_render::quality::StageQuality;
use std::time::Duration;
use url::Url;
#[derive(Default, Debug, PartialEq, Clone)]
pub struct PlayerOptions {
pub parameters: Vec<(String, String)>,
pub max_execution_duration: Option<Duration>,
pub base: Option<Url>,
pub quality: Option<StageQuality>,
pub align: Option<StageAlign>,
pub force_align: Option<bool>,
pub scale: Option<StageScaleMode>,
pub force_scale: Option<bool>,
pub upgrade_to_https: Option<bool>,
pub load_behavior: Option<LoadBehavior>,
pub letterbox: Option<Letterbox>,
pub spoof_url: Option<Url>,
pub player_version: Option<u8>,
pub player_runtime: Option<PlayerRuntime>,
pub frame_rate: Option<f64>,
pub dummy_external_interface: Option<bool>,
}
impl PlayerOptions {
pub fn or(&self, other: &Self) -> Self {
let mut parameters = other.parameters.clone();
parameters.append(&mut self.parameters.clone());
Self {
parameters,
max_execution_duration: self.max_execution_duration.or(other.max_execution_duration),
base: self.base.clone().or_else(|| other.base.clone()),
quality: self.quality.or(other.quality),
align: self.align.or(other.align),
force_align: self.force_align.or(other.force_align),
scale: self.scale.or(other.scale),
force_scale: self.force_scale.or(other.force_scale),
upgrade_to_https: self.upgrade_to_https.or(other.upgrade_to_https),
load_behavior: self.load_behavior.or(other.load_behavior),
letterbox: self.letterbox.or(other.letterbox),
spoof_url: self.spoof_url.clone().or_else(|| other.spoof_url.clone()),
player_version: self.player_version.or(other.player_version),
player_runtime: self.player_runtime.or(other.player_runtime),
frame_rate: self.frame_rate.or(other.frame_rate),
dummy_external_interface: self
.dummy_external_interface
.or(other.dummy_external_interface),
}
}
}