use adw::prelude::*; use gtk::glib; mod app_context; mod bus; mod enroll_dialog; mod error; mod firstrun; mod keys_page; mod policy_form; mod policy_page; mod recovery_page; #[cfg(feature = "totp")] mod totp_page; const APP_ID: &str = "io.dangerousthings.AuthForge"; fn main() -> glib::ExitCode { // Multi-thread tokio runtime hosts zbus's tokio-flavored futures. The // `_guard` keeps the runtime context active on this thread so calls like // `zbus::Connection::system().await` from within `glib::MainContext:: // spawn_local` closures don't panic with "no current reactor". let runtime = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() .expect("build tokio runtime"); let _guard = runtime.enter(); // Strip `--first-run` from argv before GTK's own option parser sees it; // GTK rejects unknown long options. Phase 10 fills the body of // firstrun::run; the prep lane only wires the entry point. let raw_args: Vec = std::env::args().collect(); let first_run = raw_args.iter().any(|a| a == "--first-run"); let gtk_args: Vec = raw_args .iter() .filter(|a| a.as_str() != "--first-run") .cloned() .collect(); let app = adw::Application::builder().application_id(APP_ID).build(); app.connect_activate(move |app| { let win = adw::ApplicationWindow::builder() .application(app) .default_width(720) .default_height(540) .title("Authentication") .build(); let toast_overlay = adw::ToastOverlay::new(); let ctx = app_context::AppContext::new(win.clone().upcast(), toast_overlay.clone()); if first_run { firstrun::run(ctx); return; } let header = adw::HeaderBar::new(); let stack = adw::ViewStack::new(); let switcher = adw::ViewSwitcher::builder() .stack(&stack) .policy(adw::ViewSwitcherPolicy::Wide) .build(); header.set_title_widget(Some(&switcher)); let keys = keys_page::KeysPage::new(ctx.clone()); stack.add_titled(&keys.root, Some("keys"), "Keys"); let policy = policy_page::PolicyPage::new(ctx.clone()); stack.add_titled(&policy.root, Some("policy"), "Policy"); let recovery = recovery_page::RecoveryPage::new(ctx.clone()); stack.add_titled(&recovery.root, Some("recovery"), "Recovery"); #[cfg(feature = "totp")] { let totp = totp_page::TotpPage::new(ctx.clone()); stack.add_titled(&totp.root, Some("totp"), "TOTP"); } // ToolbarView is libadwaita v1_4-gated; the project sticks with the // simpler Box layout for portability (see commit c6a5e94). let layout = gtk::Box::new(gtk::Orientation::Vertical, 0); layout.append(&header); layout.append(&stack); stack.set_vexpand(true); toast_overlay.set_child(Some(&layout)); win.set_content(Some(&toast_overlay)); win.present(); }); app.run_with_args(>k_args) }