diff --git a/Cargo.toml b/Cargo.toml index 6b8ae5d..f4c6fd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,12 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.release] +codegen-units = 1 +opt-level = "z" +strip = true +lto = true + [dependencies] anyhow = "1.0" chrono = "0.4.38" @@ -54,4 +60,4 @@ features = [ "Win32_UI", "Win32", "Win32_Graphics_Gdi", -] \ No newline at end of file +] diff --git a/README.md b/README.md index c0c4f57..97212e6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # smart-rgb -A personal script solution to ensure my computer's RGB lights go off when I need them to be. +A personal solution to ensure my computer's RGB lights go off when I need them to be. - Off - After idling during the day for more than 3 hours. - After idling at night (past 11PM) for more than 25 minutes. + - Immediately once locked from 1:30 AM to 8:00 AM. - Immediately once put into 'sleep' mode. - On - Upon unlock. diff --git a/src/main.rs b/src/main.rs index 2379ff1..aa343a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use std::ffi::OsString; +use std::{ffi::OsString}; use log::{info, debug}; use log4rs::Handle; +use openrgb::OpenRGB; use windows_service::{ service::{ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType, ServiceType}, service_control_handler::{self, ServiceControlHandlerResult}, @@ -9,36 +10,56 @@ use windows_service::{ }; use std::time::Duration; -use tokio::{runtime::Runtime, sync::mpsc}; +use tokio::{net::TcpStream, runtime::Runtime, sync::mpsc}; use tokio::sync::mpsc::UnboundedReceiver; use windows_service::{define_windows_service, service_dispatcher}; -const SERVICE_NAME: &str = "Easy RGB - Background Scheduler"; -const SERVICE_DESCRIPTION: &str = "Service to apply rules to background processes"; +const SERVICE_NAME: &str = "RGBXevion"; +const SERVICE_DESCRIPTION: &str = "Custom service to toggle RGB lights based on lock/sleep events"; + +const PROFILE_ENABLE_NAME: &str = "On"; +const PROFILE_DISABLE_NAME: &str = "Off"; define_windows_service!(ffi_service_main, service_main); -pub(crate) async fn rule_applier( - rule_file_path: &str, +pub async fn try_load_profile(client: &OpenRGB, profile_name: &str) -> anyhow::Result<()> { + let profiles = client.get_profiles().await?; + + let profile_available: bool = profiles.iter().any(|profile| profile == profile_name); + if !profile_available { + info!("Profile not found: {}", profile_name); + return Ok(()); + } + + client.load_profile(profile_name).await?; + info!("Profile set to: {}", profile_name); + + Ok(()) +} + +pub(crate) async fn profile_applier( + profile_recv: &mut UnboundedReceiver, shutdown_recv: &mut UnboundedReceiver<()>, ) -> anyhow::Result<()> { - // let wmi_con = WMIConnection::new(COMLibrary::new()?)?; - // Apply rules to all running processes - // let running_process: Vec = wmi_con.async_query().await?; - // running_process.into_iter().for_each(|process| { - // let process_info: ProcessInfo = process.into(); - // rule_set.apply(&process_info) - // }); + let client = OpenRGB::connect().await?; + client.set_name(format!("{} v{}", SERVICE_NAME, env!("CARGO_PKG_VERSION"))).await?; - tokio::select! { - // Apply rules to new processes - // output = monitor_new_processes(&rule_set, &wmi_con) => output, - // Or wait for shutdown signal - _ = shutdown_recv.recv() => { - info!("Shutting down process monitor"); - Ok(()) + loop { + tokio::select! { + enable = profile_recv.recv() => { + debug!("Received profile command: {:?}", enable); + if enable.is_none() { + continue; + } + + try_load_profile(&client, if enable.unwrap() { PROFILE_ENABLE_NAME } else { PROFILE_DISABLE_NAME }).await?; + } + _ = shutdown_recv.recv() => { + info!("Service shutting down"); + return Ok(()) + } } } } @@ -46,32 +67,47 @@ pub(crate) async fn rule_applier( #[cfg(windows)] fn service_main(_: Vec) { use windows_service::service::{ - ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus, - SessionChangeReason, + PowerEventParam, ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus, SessionChangeReason }; let rt = Runtime::new().unwrap(); + let (shutdown_send, mut shutdown_recv) = mpsc::unbounded_channel(); + let (profile_send, mut profile_recv) = mpsc::unbounded_channel::(); let event_handler = move |control_event| -> ServiceControlHandlerResult { match control_event { - ServiceControl::SessionChange(session) => { - info!( - "Session Changed: {}", - match session.reason { - SessionChangeReason::ConsoleConnect => "Console Connect", - SessionChangeReason::ConsoleDisconnect => "Console Disconnect", - SessionChangeReason::RemoteConnect => "Remote Connect", - SessionChangeReason::RemoteDisconnect => "Remote Disconnect", - SessionChangeReason::SessionLogon => "Session Logon", - SessionChangeReason::SessionLogoff => "Session Logoff", - SessionChangeReason::SessionLock => "Session Lock", - SessionChangeReason::SessionUnlock => "Session Unlock", - SessionChangeReason::SessionRemoteControl => "Session Remote Control", - SessionChangeReason::SessionCreate => "Session Create", - SessionChangeReason::SessionTerminate => "Session Terminate", + ServiceControl::PowerEvent(event) => + { + debug!("Power event: {:?}", event); + match event { + PowerEventParam::QuerySuspend => { + // Send false to disable RGB + profile_send.send(false).unwrap(); } - ); + PowerEventParam::ResumeSuspend | PowerEventParam::QuerySuspendFailed => { + // Send true to enable RGB + profile_send.send(true).unwrap(); + } + _ => {} + } + + ServiceControlHandlerResult::NoError + } + ServiceControl::SessionChange(change) => { + debug!("Session change: {:?}", change); + + match change.reason { + SessionChangeReason::SessionLock => { + // Send false to disable RGB + profile_send.send(false).unwrap(); + } + SessionChangeReason::SessionUnlock => { + // Send true to enable RGB + profile_send.send(true).unwrap(); + } + _ => {} + } ServiceControlHandlerResult::NoError } @@ -89,7 +125,7 @@ fn service_main(_: Vec) { .set_service_status(ServiceStatus { service_type: ServiceType::OWN_PROCESS, current_state: ServiceState::Running, - controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SESSION_CHANGE, + controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SESSION_CHANGE | ServiceControlAccept::POWER_EVENT, exit_code: ServiceExitCode::Win32(0), checkpoint: 0, wait_hint: Duration::default(), @@ -101,7 +137,7 @@ fn service_main(_: Vec) { let rules_path = args.get(2).map(|s| s.as_str()).unwrap_or("rules.json"); let error_code = if rt - .block_on(rule_applier(rules_path, &mut shutdown_recv)) + .block_on(profile_applier(&mut profile_recv, &mut shutdown_recv)) .is_err() { 1 @@ -170,7 +206,7 @@ fn main() -> anyhow::Result<(), windows_service::Error> { if let Some(command) = command { match command.as_str() { "install" => { - install_service(args.get(2).map(|s| s.as_str()))?; + install_service()?; info!("Service installed"); return Ok(()); } @@ -200,7 +236,7 @@ fn main() -> anyhow::Result<(), windows_service::Error> { } #[cfg(windows)] -fn install_service(rules_path: Option<&str>) -> windows_service::Result<()> { +fn install_service() -> windows_service::Result<()> { let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE; let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?; let service_binary_path = ::std::env::current_exe().unwrap();