| 1 | //! remage: a keyboard-centric image viewer. |
| 2 | |
| 3 | mod action; |
| 4 | mod anim; |
| 5 | mod app; |
| 6 | mod cache; |
| 7 | mod cli; |
| 8 | mod config; |
| 9 | mod decode; |
| 10 | mod exec; |
| 11 | mod input; |
| 12 | mod ipc; |
| 13 | mod render; |
| 14 | mod scan; |
| 15 | mod state; |
| 16 | mod thumbs; |
| 17 | mod ui; |
| 18 | mod worker; |
| 19 | |
| 20 | use std::time::Instant; |
| 21 | |
| 22 | use clap::Parser; |
| 23 | use winit::event_loop::EventLoop; |
| 24 | |
| 25 | use app::{App, Options, UserEvent}; |
| 26 | use state::queue::Queue; |
| 27 | use state::{AppState, MsgKind, Settings}; |
| 28 | |
| 29 | fn main() -> anyhow::Result<()> { |
| 30 | let started = Instant::now(); |
| 31 | env_logger::init(); |
| 32 | let cli = cli::Cli::parse(); |
| 33 | log::info!("remage {}", env!("CARGO_PKG_VERSION")); |
| 34 | |
| 35 | let scan = scan::scan( |
| 36 | &cli.paths, |
| 37 | &scan::ScanOptions { |
| 38 | recursive: cli.recursive, |
| 39 | siblings: !cli.no_siblings, |
| 40 | sort: cli.sort, |
| 41 | reverse: cli.reverse, |
| 42 | }, |
| 43 | ); |
| 44 | log::debug!( |
| 45 | "scanned {} files ({} to sniff) in {:.1?}", |
| 46 | scan.files.len(), |
| 47 | scan.unknown.len(), |
| 48 | started.elapsed() |
| 49 | ); |
| 50 | |
| 51 | let settings = Settings { |
| 52 | fit: cli.fit.unwrap_or(Settings::default().fit), |
| 53 | zoom: cli.zoom.map(|z| z / 100.0), |
| 54 | ..Settings::default() |
| 55 | }; |
| 56 | let start_index = cli.start.unwrap_or(1).saturating_sub(1); |
| 57 | let queue = Queue::new(scan.files, scan.start.as_deref(), start_index); |
| 58 | let mut state = AppState::new(queue, settings, !cli.no_bar, Instant::now()); |
| 59 | if let Some(err) = scan.errors.first() { |
| 60 | state.notify(format!("error: {err}"), MsgKind::Error, Instant::now()); |
| 61 | } |
| 62 | |
| 63 | let event_loop = EventLoop::<UserEvent>::with_user_event().build()?; |
| 64 | let proxy = event_loop.create_proxy(); |
| 65 | if !scan.unknown.is_empty() { |
| 66 | let proxy = proxy.clone(); |
| 67 | let unknown = scan.unknown; |
| 68 | std::thread::spawn(move || { |
| 69 | let found: Vec<_> = unknown.into_iter().filter(|p| decode::sniff(p)).collect(); |
| 70 | if !found.is_empty() { |
| 71 | let _ = proxy.send_event(UserEvent::Found(found)); |
| 72 | } |
| 73 | }); |
| 74 | } |
| 75 | |
| 76 | let opts = Options { |
| 77 | fullscreen: cli.fullscreen, |
| 78 | geometry: cli.geometry, |
| 79 | class: cli.class, |
| 80 | cache_mb: 512, |
| 81 | }; |
| 82 | let mut app = App::new(opts, state, proxy); |
| 83 | event_loop.run_app(&mut app)?; |
| 84 | match app.error.take() { |
| 85 | Some(e) => Err(e), |
| 86 | None => Ok(()), |
| 87 | } |
| 88 | } |