Files
OneCAD-Draft/src/main.rs
T
Hakan Seven b90b3e3ecf feat(locale): add Fluent translations
Default to the platform language with persisted overrides across native and web builds.

Refs #630
2026-08-02 18:53:38 +03:00

171 lines
6.7 KiB
Rust

#![allow(non_snake_case)]
// On Windows release builds, hide the console window the OS would
// otherwise spawn alongside the GUI. Debug builds keep stdout/stderr
// attached so eprintln! / panics stay visible while developing.
#![cfg_attr(all(windows, not(debug_assertions)), windows_subsystem = "windows")]
mod app;
mod config;
#[cfg(not(target_arch = "wasm32"))]
mod cli;
mod command;
mod entities;
mod i18n;
mod io;
#[cfg(not(target_arch = "wasm32"))]
mod network;
mod modules;
mod patreon;
mod discussions;
mod videos;
mod plugin;
mod perf;
mod scene;
mod snap;
mod ui;
mod par;
mod sys;
fn main() -> iced::Result {
// Web (wasm) uses the single-window entry; native uses the multi-window
// daemon. Trunk calls `main` from its generated JS bootstrap. The web build
// takes no CLI args, so it skips parsing entirely.
#[cfg(target_arch = "wasm32")]
{
console_error_panic_hook::set_once();
// After the console hook so the chained panic mirror keeps it; also
// installs the log-facade listener that surfaces wgpu/naga errors as
// a copyable on-page banner (#414 — an empty canvas otherwise gives
// reporters nothing to paste).
sys::web_diag::init();
return app::run_web();
}
#[cfg(not(target_arch = "wasm32"))]
{
use clap::Parser;
let args = cli::Cli::parse();
// Plugin runner mode: the host spawns itself with this hidden flag to
// load a plugin cdylib in an isolated process. Hand off immediately so
// the child never touches GUI state.
if let Some(runner_args) = &args.ocs_plugin_runner {
if runner_args.len() != 2 {
eprintln!("--ocs-plugin-runner expects <socket> <cdylib>");
std::process::exit(1);
}
let socket = &runner_args[0];
let cdylib = std::path::Path::new(&runner_args[1]);
if let Err(e) = ocs_plugin_api::runner::run(socket, cdylib) {
eprintln!("[runner] fatal: {e}");
std::process::exit(1);
}
return Ok(());
}
// Thumbnail mode: the OS file-manager thumbnailer invokes us as
// `--dwg-thumbnail <in> <out> <size>`. Extract the DWG's embedded
// preview to a PNG and exit — never touch the GUI.
if let Some(a) = &args.dwg_thumbnail {
let size = a.get(2).and_then(|s| s.parse().ok()).unwrap_or(256);
let ok = io::thumbnail::extract_to_png(
std::path::Path::new(&a[0]),
std::path::Path::new(&a[1]),
size,
);
std::process::exit(if ok { 0 } else { 1 });
}
// Opt-in logging. `--log LEVEL` seeds RUST_LOG; the subscriber then
// surfaces wgpu / iced / winit diagnostics that are otherwise silent.
if let Some(level) = &args.log {
std::env::set_var("RUST_LOG", level);
}
if std::env::var_os("RUST_LOG").is_some() {
let _ = env_logger::try_init();
}
// GPU backend selection. Explicit `--backend` wins; `--safe-mode`
// forces GL for flaky drivers. On Windows, fall back to DX12/Vulkan so
// the AMD OpenGL ICD (atio6axx.dll) is never touched at startup — it
// access-violates on some hybrid-GPU laptops before any window appears
// (#55). An already-set WGPU_BACKEND always wins.
if let Some(backend) = &args.backend {
std::env::set_var("WGPU_BACKEND", backend);
} else if args.safe_mode {
std::env::set_var("WGPU_BACKEND", "gl");
}
#[cfg(target_os = "windows")]
if std::env::var_os("WGPU_BACKEND").is_none() {
std::env::set_var("WGPU_BACKEND", "dx12,vulkan");
}
// Headless modes exit without ever creating a window.
if args.serve {
// `app::serve` reads --port itself from the raw args.
app::serve();
return Ok(());
}
if let Some(io) = &args.export {
// clap enforces exactly two values for --export.
let code = app::export_headless(&io[0], &io[1]);
std::process::exit(code);
}
// Single instance: a double-clicked drawing belongs as a tab in the
// editor that is already open, not in a second copy of the app.
//
// The gate is POSITIONAL, and that is the point: every headless mode
// has already returned above — the plugin runner, which is this same
// binary re-spawning itself, most of all. A flag list here would rot
// the first time a mode is added; a position cannot.
if !args.new_instance {
if let io::single_instance::Claim::Existing(stream) = io::single_instance::claim() {
// Only bare files forward. `--read-only` / `--script` / `--new`
// configure the whole editor rather than a tab, so they always
// get a process of their own.
let plain_open =
!args.read_only && args.script.is_none() && !args.new && !args.files.is_empty();
if plain_open && io::single_instance::handoff(stream, &args.files) {
return Ok(());
}
// Nothing to forward, or the far end never acknowledged: fall
// through and boot our own window. We hold no listener, so the
// editor that owns the port keeps serving.
}
}
// GUI: stash the startup config for `app::boot` to pick up.
let script_lines = args
.script
.as_ref()
.map(|p| match std::fs::read_to_string(p) {
Ok(text) => text
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.map(str::to_string)
.collect(),
Err(e) => {
eprintln!("--script: cannot read {}: {e}", p.display());
Vec::new()
}
})
.unwrap_or_default();
let _ = cli::GUI_CONFIG.set(cli::GuiConfig {
files: if args.new { Vec::new() } else { args.files },
new: args.new,
read_only: args.read_only,
compat_renderer: args.compat_renderer,
script_lines,
});
// Register (or refresh) the freedesktop DWG thumbnailer so file managers
// show OCS-embedded previews. Idempotent, best-effort, no consent step —
// it only points a `.thumbnailer` at this same binary's `--dwg-thumbnail`
// mode. Silently ignored on failure or non-Linux.
io::file_association::install_thumbnailer();
app::run()
}
}