Allow config to be updated with CLI args
This commit is contained in:
parent
99595eeeed
commit
028fd83296
3 changed files with 120 additions and 15 deletions
|
|
@ -4,7 +4,9 @@ use cosmic::{
|
||||||
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry},
|
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry},
|
||||||
theme,
|
theme,
|
||||||
};
|
};
|
||||||
|
use lexopt::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{path::PathBuf, process};
|
||||||
|
|
||||||
use crate::wrappers::HWDeviceType;
|
use crate::wrappers::HWDeviceType;
|
||||||
|
|
||||||
|
|
@ -41,3 +43,52 @@ impl Default for Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn with_args(&mut self, args: &mut Args) {
|
||||||
|
if let Some(decoder) = args.decoder {
|
||||||
|
self.hw_decoder = decoder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Args {
|
||||||
|
pub paths: Vec<PathBuf>,
|
||||||
|
pub decoder: Option<HWDeviceType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Args {
|
||||||
|
pub fn parse_args() -> Result<Self, lexopt::Error> {
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
let mut decoder = None;
|
||||||
|
|
||||||
|
let mut parser = lexopt::Parser::from_env();
|
||||||
|
while let Some(arg) = parser.next()? {
|
||||||
|
match arg {
|
||||||
|
Long("list-hwdec") => {
|
||||||
|
println!("Supported hardware decoders:");
|
||||||
|
for hwdec in HWDeviceType::supported_devices() {
|
||||||
|
println!("\t* [{}] {hwdec}", hwdec.short_name());
|
||||||
|
}
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
Long("hwdec") => {
|
||||||
|
decoder = Some(parser.value()?.parse()?);
|
||||||
|
}
|
||||||
|
Value(path) => {
|
||||||
|
let path = path.parse()?;
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
_ => return Err(arg.unexpected()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if paths.is_empty() {
|
||||||
|
return Err(lexopt::Error::MissingValue {
|
||||||
|
option: Some("missing video path".into()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { paths, decoder })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
29
src/main.rs
29
src/main.rs
|
|
@ -16,14 +16,12 @@ use cosmic::{
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
env,
|
env, process,
|
||||||
path::PathBuf,
|
|
||||||
process,
|
|
||||||
sync::{mpsc, Arc, Mutex},
|
sync::{mpsc, Arc, Mutex},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use config::{AppTheme, Config, CONFIG_VERSION};
|
use config::{AppTheme, Args, Config, CONFIG_VERSION};
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
use key_bind::{key_binds, KeyBind};
|
use key_bind::{key_binds, KeyBind};
|
||||||
|
|
@ -43,15 +41,25 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
localize::localize();
|
localize::localize();
|
||||||
|
|
||||||
|
let mut args = match Args::parse_args() {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let (config_handler, config) = match cosmic_config::Config::new(App::APP_ID, CONFIG_VERSION) {
|
let (config_handler, config) = match cosmic_config::Config::new(App::APP_ID, CONFIG_VERSION) {
|
||||||
Ok(config_handler) => {
|
Ok(config_handler) => {
|
||||||
let config = match Config::get_entry(&config_handler) {
|
let mut config = match Config::get_entry(&config_handler) {
|
||||||
Ok(ok) => ok,
|
Ok(ok) => ok,
|
||||||
Err((errs, config)) => {
|
Err((errs, config)) => {
|
||||||
log::info!("errors loading config: {:?}", errs);
|
log::info!("errors loading config: {:?}", errs);
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Update config with command line args
|
||||||
|
config.with_args(&mut args);
|
||||||
(Some(config_handler), config)
|
(Some(config_handler), config)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -60,14 +68,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: support multiple paths
|
//TODO: support using multiple paths
|
||||||
let path = match env::args().skip(1).next() {
|
let Args { mut paths, .. } = args;
|
||||||
Some(arg) => PathBuf::from(arg),
|
let path = paths.pop().unwrap();
|
||||||
None => {
|
|
||||||
log::error!("no argument provided");
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (player_tx, video_queue_lock) = player::run(path);
|
let (player_tx, video_queue_lock) = player::run(path);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::{fmt, iter::FusedIterator, str::FromStr};
|
||||||
|
|
||||||
use ffmpeg_next::ffi::{av_hwdevice_iterate_types, AVHWDeviceType};
|
use ffmpeg_next::ffi::{av_hwdevice_iterate_types, AVHWDeviceType};
|
||||||
use serde::{
|
use serde::{
|
||||||
|
|
@ -25,6 +25,8 @@ pub enum HWDeviceType {
|
||||||
/// DirectX Video Acceleration 2.0
|
/// DirectX Video Acceleration 2.0
|
||||||
/// https://learn.microsoft.com/en-us/windows/win32/medfound/about-dxva-2-0
|
/// https://learn.microsoft.com/en-us/windows/win32/medfound/about-dxva-2-0
|
||||||
Dxva2,
|
Dxva2,
|
||||||
|
/// Direct Rendering Manager
|
||||||
|
/// https://dri.freedesktop.org/wiki/DRM/
|
||||||
Drm,
|
Drm,
|
||||||
/// MediaCodec
|
/// MediaCodec
|
||||||
/// Android only
|
/// Android only
|
||||||
|
|
@ -70,7 +72,26 @@ impl HWDeviceType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Supported hardware decoders
|
/// Short name for CLI arguments
|
||||||
|
pub const fn short_name(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::None => "none",
|
||||||
|
Self::Cuda => "cuda",
|
||||||
|
Self::Dxva2 => "dxva2",
|
||||||
|
Self::D3d11va => "d3d11va",
|
||||||
|
Self::D3d12va => "d3d12va",
|
||||||
|
Self::Drm => "drm",
|
||||||
|
Self::MediaCodec => "mediacodec",
|
||||||
|
Self::OpenCl => "opencl",
|
||||||
|
Self::Qsv => "qsv",
|
||||||
|
Self::Vaapi => "vaapi",
|
||||||
|
Self::Vdpau => "vdpau",
|
||||||
|
Self::VideoToolbox => "videotoolbox",
|
||||||
|
Self::Vulkan => "vulkan",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// System's supported hardware decoders
|
||||||
pub fn supported_devices() -> SupportedDeviceIter {
|
pub fn supported_devices() -> SupportedDeviceIter {
|
||||||
SupportedDeviceIter::default()
|
SupportedDeviceIter::default()
|
||||||
}
|
}
|
||||||
|
|
@ -104,6 +125,12 @@ impl FromStr for HWDeviceType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for HWDeviceType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<AVHWDeviceType> for HWDeviceType {
|
impl From<AVHWDeviceType> for HWDeviceType {
|
||||||
fn from(value: AVHWDeviceType) -> Self {
|
fn from(value: AVHWDeviceType) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
|
@ -131,6 +158,7 @@ impl Default for HWDeviceType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterator over system's supported hardware decoders.
|
||||||
pub struct SupportedDeviceIter {
|
pub struct SupportedDeviceIter {
|
||||||
current: AVHWDeviceType,
|
current: AVHWDeviceType,
|
||||||
}
|
}
|
||||||
|
|
@ -147,13 +175,36 @@ impl Iterator for SupportedDeviceIter {
|
||||||
type Item = HWDeviceType;
|
type Item = HWDeviceType;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
// None is a sentinel value that indicates the iterator is exhausted
|
||||||
if self.current == AVHWDeviceType::AV_HWDEVICE_TYPE_NONE {
|
if self.current == AVHWDeviceType::AV_HWDEVICE_TYPE_NONE {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let prev = self.current;
|
let prev = self.current;
|
||||||
|
// SAFETY: The docs and examples state that the iterator yields the next value
|
||||||
|
// when the previous is passed in.
|
||||||
self.current = unsafe { av_hwdevice_iterate_types(prev) };
|
self.current = unsafe { av_hwdevice_iterate_types(prev) };
|
||||||
|
|
||||||
Some(prev.into())
|
Some(prev.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FusedIterator for SupportedDeviceIter {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::hint::black_box;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// The iterator's yielded values aren't important since hardware decoders vary by system
|
||||||
|
// This is just a sanity check to ensure the iterator works
|
||||||
|
#[test]
|
||||||
|
fn supported_device_iter_doesnt_seg_fault() {
|
||||||
|
for decoder in HWDeviceType::supported_devices() {
|
||||||
|
black_box(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _decoders: Vec<_> = black_box(HWDeviceType::supported_devices().collect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue