diff --git a/Cargo.lock b/Cargo.lock index bba2fcb..c059a04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3092,6 +3092,30 @@ dependencies = [ "objc2-foundation 0.3.2", ] +[[package]] +name = "objc2-security" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" +dependencies = [ + "bitflags 2.11.0", + "objc2 0.6.3", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-service-management" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b213642d6959cc6023ceb1217aa595eaaf09b8094ce95127c103cab611fe65e8" +dependencies = [ + "block2 0.6.2", + "objc2 0.6.3", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-security", +] + [[package]] name = "objc2-symbols" version = "0.2.2" @@ -3844,6 +3868,7 @@ dependencies = [ "objc2-application-services", "objc2-core-foundation", "objc2-foundation 0.3.2", + "objc2-service-management", "once_cell", "rand", "rayon", diff --git a/Cargo.toml b/Cargo.toml index a996831..82cb240 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ objc2-application-services = { version = "0.3.2", default-features = false, feat ] } objc2-core-foundation = "0.3.2" objc2-foundation = { version = "0.3.2", features = ["NSString"] } +objc2-service-management = "0.3.2" once_cell = "1.21.3" rand = "0.9.2" rayon = "1.11.0" diff --git a/src/app.rs b/src/app.rs index dc90071..516bb37 100644 --- a/src/app.rs +++ b/src/app.rs @@ -81,6 +81,7 @@ pub enum Editable { pub enum Message { WriteConfig(bool), SaveRanking, + ToggleAutoStartup(bool), LoadRanking, ToggleFavouriteApp(String), UpdateAvailable, diff --git a/src/app/pages/settings.rs b/src/app/pages/settings.rs index 71742ba..c9cdbd7 100644 --- a/src/app/pages/settings.rs +++ b/src/app/pages/settings.rs @@ -102,6 +102,16 @@ pub fn settings_page(config: Config) -> Element<'static, Message> { ), ]); + let theme_clone = theme.clone(); + let start_at_login = settings_item_row([ + settings_hint_text(theme.clone(), "Start at login"), + checkbox(config.clone().start_at_login) + .style(move |_, _| settings_checkbox_style(&theme_clone)) + .on_toggle(Message::ToggleAutoStartup) + .into(), + notice_item(theme.clone(), "If you want rustcast to start on login"), + ]); + let theme_clone = theme.clone(); let haptic = Row::from_iter([ settings_hint_text(theme.clone(), "Haptic feedback"), @@ -394,6 +404,7 @@ pub fn settings_page(config: Config) -> Element<'static, Message> { placeholder_setting.into(), search.into(), debounce.into(), + start_at_login.into(), haptic.into(), tray_icon.into(), auto_suggest.into(), diff --git a/src/app/tile/update.rs b/src/app/tile/update.rs index 8cac396..a65e26c 100644 --- a/src/app/tile/update.rs +++ b/src/app/tile/update.rs @@ -34,6 +34,7 @@ use crate::commands::Function; use crate::config::Config; use crate::config::MainPage; use crate::debounce::DebouncePolicy; +use crate::platform::macos::{start_at_login, stop_at_login}; use crate::quit::get_open_apps; use crate::unit_conversion; use crate::utils::is_valid_url; @@ -96,6 +97,17 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task { Task::none() } + Message::ToggleAutoStartup(set_to) => { + if set_to { + start_at_login(); + tile.config.start_at_login = true + } else { + stop_at_login(); + tile.config.start_at_login = false + } + Task::none() + } + Message::EscKeyPressed(id) => { if !tile.query_lc.is_empty() { return Task::batch([ diff --git a/src/config.rs b/src/config.rs index 667fa76..2a3f506 100644 --- a/src/config.rs +++ b/src/config.rs @@ -21,6 +21,7 @@ pub struct Config { pub clipboard_hotkey: String, pub buffer_rules: Buffer, pub main_page: MainPage, + pub start_at_login: bool, pub theme: Theme, pub placeholder: String, pub search_url: String, @@ -42,6 +43,7 @@ impl Default for Config { clipboard_hotkey: "SUPER+SHIFT+C".to_string(), buffer_rules: Buffer::default(), theme: Theme::default(), + start_at_login: true, placeholder: String::from("Time to be productive!"), search_url: "https://duckduckgo.com/search?q=%s".to_string(), haptic_feedback: false, diff --git a/src/main.rs b/src/main.rs index 5497ea2..6b06a7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use std::{fs::OpenOptions, path::Path}; use crate::{ app::tile::{self, Tile}, config::Config, + platform::macos::get_autostart_status, }; use global_hotkey::{ @@ -43,11 +44,13 @@ fn main() -> iced::Result { .unwrap(); } - let config: Config = match std::fs::read_to_string(&file_path) { + let mut config: Config = match std::fs::read_to_string(&file_path) { Ok(a) => toml::from_str(&a).unwrap_or(Config::default()), Err(_) => Config::default(), }; + config.start_at_login = get_autostart_status(); + if cfg!(debug_assertions) { let sub = tracing_subscriber::fmt().finish(); EnvFilter::new("rustcast=info").with_subscriber(sub).init(); diff --git a/src/platform/macos/login.rs b/src/platform/macos/login.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index e8a8856..ab7c05c 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -7,6 +7,31 @@ use iced::wgpu::rwh::WindowHandle; pub(super) use self::discovery::get_installed_apps; pub(super) use self::haptics::perform_haptic; +use objc2_service_management::SMAppService; + +pub fn start_at_login() { + unsafe { + SMAppService::mainAppService().registerAndReturnError().ok(); + } +} + +pub fn stop_at_login() { + unsafe { + SMAppService::mainAppService() + .unregisterAndReturnError() + .ok(); + } +} + +pub fn get_autostart_status() -> bool { + unsafe { + SMAppService::mainAppService() + .registerAndReturnError() + .ok() + .is_some() + } +} + /// This sets the activation policy of the app to Accessory, allowing rustcast to be visible ontop /// of fullscreen apps pub(super) fn set_activation_policy_accessory() {