1use cosmic::app::Settings;
5use cosmic::iced::Limits;
6use std::path::PathBuf;
7use std::{env, fs};
8#[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))]
9use std::process;
10use tracing_subscriber::layer::SubscriberExt;
11use tracing_subscriber::util::SubscriberInitExt;
12
13use crate::app::{App, Flags};
14use crate::config::{Config, State};
15use crate::tab::Location;
16
17pub mod app;
18mod archive;
19pub mod channel;
20pub mod clipboard;
21pub mod config;
22mod context_action;
23pub mod dialog;
24mod key_bind;
25pub(crate) mod large_image;
26pub(crate) mod load_image;
27mod localize;
28mod menu;
29mod mime_app;
30pub mod mime_icon;
31mod mounter;
32mod mouse_area;
33pub mod operation;
34pub mod russh;
35mod spawn_detached;
36pub mod tab;
37mod thumbnail_cacher;
38mod thumbnailer;
39pub mod sequencing;
40pub(crate) mod trash;
41mod zoom;
42
43pub(crate) type FxOrderMap<K, V> = ordermap::OrderMap<K, V, rustc_hash::FxBuildHasher>;
44
45#[allow(dead_code)]
46pub(crate) fn err_str<T: ToString>(err: T) -> String {
47 err.to_string()
48}
49
50pub fn desktop_dir() -> PathBuf {
51 if let Some(path) = dirs::desktop_dir() {
52 path
53 } else {
54 let path = home_dir().join("Desktop");
55 log::warn!(
56 "failed to locate desktop directory, falling back to {}",
57 path.display()
58 );
59 path
60 }
61}
62
63pub fn home_dir() -> PathBuf {
64 if let Some(home) = dirs::home_dir() {
65 home
66 } else {
67 let path = PathBuf::from("/");
68 log::warn!(
69 "failed to locate home directory, falling back to {}",
70 path.display()
71 );
72 path
73 }
74}
75
76pub fn is_wayland() -> bool {
77 matches!(
78 cosmic::app::cosmic::windowing_system(),
79 Some(cosmic::app::cosmic::WindowingSystem::Wayland)
80 )
81}
82
83#[rustfmt::skip]
85pub fn desktop() -> Result<(), Box<dyn std::error::Error>> {
86 let log_format = tracing_subscriber::fmt::format()
87 .pretty()
88 .without_time()
89 .with_line_number(true)
90 .with_file(true)
91 .with_target(false)
92 .with_thread_names(true);
93
94 let log_layer = tracing_subscriber::fmt::Layer::default()
95 .with_writer(std::io::stderr)
96 .event_format(log_format);
97
98 tracing_subscriber::registry()
99 .with(tracing_subscriber::EnvFilter::from_env("RUST_LOG"))
100 .with(log_layer)
101 .init();
102
103 localize::localize();
104
105 let (config_handler, config) = Config::load();
106 let (state_handler, state) = State::load();
107
108 let mut settings = Settings::default();
109 settings = settings.theme(config.app_theme.theme());
110 settings = settings.size_limits(Limits::NONE.min_width(360.0).min_height(180.0));
111 settings = settings.exit_on_close(false);
112 settings = settings.transparent(true);
113 #[cfg(all(feature = "wayland", feature = "desktop-applet"))]
114 {
115 settings = settings.no_main_window(true);
116 }
117
118 let locations = vec![tab::Location::Desktop(desktop_dir(), String::new(), config.desktop)];
119 let flags = Flags {
120 config_handler,
121 config,
122 state_handler,
123 state,
124 mode: app::Mode::Desktop,
125 locations,
126 uris: Vec::new()
127 };
128 cosmic::app::run::<App>(settings, flags)?;
129
130 Ok(())
131}
132
133#[rustfmt::skip]
135pub fn main() -> Result<(), Box<dyn std::error::Error>> {
136 let log_format = tracing_subscriber::fmt::format()
137 .pretty()
138 .with_line_number(true)
139 .with_file(true)
140 .with_target(false)
141 .with_thread_names(true);
142
143 let log_layer = tracing_subscriber::fmt::Layer::default()
144 .with_writer(std::io::stderr)
145 .event_format(log_format);
146
147 tracing_subscriber::registry()
148 .with(tracing_subscriber::EnvFilter::from_default_env())
149 .with(log_layer)
150 .init();
151
152 localize::localize();
153
154 let (config_handler, config) = Config::load();
155 let (state_handler, state) = State::load();
156
157 let mut daemonize = true;
158 let mut locations = Vec::new();
159 let mut uris = Vec::new();
160 for arg in env::args().skip(1) {
161 let location = if &arg == "--no-daemon" {
162 daemonize = false;
163 continue;
164 } else if &arg == "--trash" {
165 Location::Trash
166 } else if &arg == "--recents" {
167 if config.show_recents {
168 Location::Recents
169 } else {
170 log::warn!("recents feature is disabled in config");
171 continue;
172 }
173 } else if &arg == "--network" {
174 Location::Network("network:///".to_string(), fl!("networks"), None)
175 } else {
176 let path = match url::Url::parse(&arg) {
178 Ok(url) if url.scheme() == "file" => if let Ok(path) = url.to_file_path() { path } else {
179 log::warn!("invalid argument {arg:?}");
180 continue;
181 },
182 Ok(url) => {
183 uris.push(url);
184 continue;
185 }
186 _ => PathBuf::from(arg),
187 };
188 match fs::canonicalize(&path) {
189 Ok(absolute) => Location::Path(absolute),
190 Err(err) => {
191 log::warn!("failed to canonicalize {}: {}", path.display(), err);
192 continue;
193 }
194 }
195 };
196 locations.push(location);
197 }
198
199 if daemonize {
200 #[cfg(all(unix, not(any(target_os = "macos", target_os = "redox"))))]
201 match fork::daemon(true, true) {
202 Ok(fork::Fork::Child) => (),
203 Ok(fork::Fork::Parent(_child_pid)) => process::exit(0),
204 Err(err) => {
205 eprintln!("failed to daemonize: {err:?}");
206 process::exit(1);
207 }
208 }
209 }
210
211 let mut settings = Settings::default();
212 settings = settings.theme(config.app_theme.theme());
213 settings = settings.size_limits(Limits::NONE.min_width(360.0).min_height(180.0));
214 settings = settings.exit_on_close(false);
215
216 #[cfg(feature = "jemalloc")]
217 {
218 settings = settings.default_mmap_threshold(None);
219 }
220
221 let flags = Flags {
222 config_handler,
223 config,
224 state_handler,
225 state,
226 mode: app::Mode::App,
227 locations,
228 uris
229 };
230 cosmic::app::run::<App>(settings, flags)?;
231
232 Ok(())
233}