5f6e55a15664c1f0c15227a43c119a9f9cef291f
[temp2RGB.git] / src / main.rs
1 #[macro_use]
2 extern crate windows_service;
3
4 use std::{
5 collections::HashMap,
6 env,
7 ffi::OsString,
8 sync::{
9 atomic::{AtomicBool, Ordering},
10 Arc,
11 },
12 thread::sleep,
13 time::{self, Duration},
14 };
15
16 use anyhow::Result;
17 use log::{error, info, trace, warn, debug};
18 use windows::Win32::Foundation::{ERROR_SERVICE_DOES_NOT_EXIST, WIN32_ERROR};
19 use windows_service::{
20 service::{
21 ServiceAccess, ServiceControl, ServiceControlAccept, ServiceErrorControl, ServiceExitCode,
22 ServiceInfo, ServiceStartType, ServiceState, ServiceStatus, ServiceType,
23 },
24 service_control_handler::{self, ServiceControlHandlerResult, ServiceStatusHandle},
25 service_dispatcher,
26 service_manager::{ServiceManager, ServiceManagerAccess},
27 };
28 use wmi::{COMLibrary, Variant, WMIConnection};
29
30 use crate::rgb::RGB;
31
32 define_windows_service!(ffi_service_main, service_main);
33
34 mod winring0 {
35 #![allow(warnings, unused)]
36 include!(concat!(env!("OUT_DIR"), "/ols_api.rs"));
37 }
38
39 mod intel_arc {
40 #![allow(warnings, unused)]
41 include!(concat!(env!("OUT_DIR"), "/intel_arc.rs"));
42 }
43
44 mod a770;
45 mod b650_e;
46 mod machine;
47 mod main_loop;
48 // mod common;
49 mod consts;
50 mod corsair_vengeance;
51 mod piix4_i2c;
52 mod rgb;
53 // mod roccat; Disabled.
54 mod sensors_jiji;
55 mod settings;
56 mod timer;
57
58 fn main() -> Result<()> {
59 let is_debug = cfg!(debug_assertions);
60
61 flexi_logger::Logger::try_with_str(if is_debug { "debug" } else { "info" })?
62 .log_to_file(
63 flexi_logger::FileSpec::default()
64 .directory(dirs::config_dir().unwrap().join(consts::SERVICE_NAME))
65 .basename(consts::SERVICE_NAME),
66 )
67 .duplicate_to_stdout(flexi_logger::Duplicate::All)
68 .format(if is_debug { flexi_logger::default_format } else { flexi_logger::detailed_format })
69 .rotate(
70 flexi_logger::Criterion::Size(1024 * 1024),
71 flexi_logger::Naming::Timestamps,
72 flexi_logger::Cleanup::KeepLogFiles(10),
73 )
74 .print_message()
75 .start()?;
76
77 let args: Vec<String> = env::args().collect();
78
79 info!("Temperature to RGB");
80
81 if args.contains(&"--no-service".to_string()) {
82 let completed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
83 main_loop::main_loop(completed.clone());
84 } else if args.contains(&"--tests".to_string()) {
85 tests();
86 } else if args.contains(&"--install-service".to_string()) {
87 println!("Installing service...");
88 install_service()?;
89 } else if args.contains(&"--uninstall-service".to_string()) {
90 println!("Uninstalling service...");
91 uninstall_service()?;
92 } else {
93 service_dispatcher::start(consts::SERVICE_NAME, ffi_service_main)?;
94 }
95
96 Ok(())
97 }
98
99 fn install_service() -> windows_service::Result<()> {
100 let manager_access = ServiceManagerAccess::CONNECT | ServiceManagerAccess::CREATE_SERVICE;
101 let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
102
103 let service_binary_path = std::env::current_exe()
104 .unwrap()
105 .with_file_name("temp_2_rgb.exe");
106
107 println!("Installing service: {service_binary_path:?}");
108
109 let service_info = ServiceInfo {
110 name: OsString::from(consts::SERVICE_NAME),
111 display_name: OsString::from(consts::SERVICE_NAME),
112 service_type: ServiceType::OWN_PROCESS,
113 start_type: ServiceStartType::AutoStart,
114 error_control: ServiceErrorControl::Normal,
115 executable_path: service_binary_path,
116 launch_arguments: vec![],
117 dependencies: vec![],
118 account_name: None, // run as System
119 account_password: None,
120 };
121 let service = service_manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
122 service.set_description(
123 "A service to set the color of hardware according to the temperature of GPU and CPU",
124 )?;
125 Ok(())
126 }
127
128 fn uninstall_service() -> windows_service::Result<()> {
129 let manager_access = ServiceManagerAccess::CONNECT;
130 let service_manager = ServiceManager::local_computer(None::<&str>, manager_access)?;
131
132 let service_access = ServiceAccess::QUERY_STATUS | ServiceAccess::STOP | ServiceAccess::DELETE;
133 let service = service_manager.open_service(consts::SERVICE_NAME, service_access)?;
134
135 // The service will be marked for deletion as long as this function call succeeds.
136 // However, it will not be deleted from the database until it is stopped and all open handles to it are closed.
137 service.delete()?;
138
139 // Our handle to it is not closed yet. So we can still query it.
140 if service.query_status()?.current_state != ServiceState::Stopped {
141 // If the service cannot be stopped, it will be deleted when the system restarts.
142 service.stop()?;
143 }
144
145 // Explicitly close our open handle to the service. This is automatically called when `service` goes out of scope.
146 drop(service);
147
148 // Win32 API does not give us a way to wait for service deletion.
149 // To check if the service is deleted from the database, we have to poll it ourselves.
150 let start = time::Instant::now();
151 let timeout = Duration::from_secs(5);
152 while start.elapsed() < timeout {
153 if let Err(windows_service::Error::Winapi(e)) =
154 service_manager.open_service(consts::SERVICE_NAME, ServiceAccess::QUERY_STATUS)
155 {
156 let WIN32_ERROR(error_num) = ERROR_SERVICE_DOES_NOT_EXIST;
157 if e.raw_os_error() == Some(error_num as i32) {
158 println!("{} is deleted.", consts::SERVICE_NAME);
159 return Ok(());
160 }
161 }
162 sleep(Duration::from_secs(1));
163 }
164 println!("{} is marked for deletion.", consts::SERVICE_NAME);
165
166 Ok(())
167 }
168
169 fn service_main(arguments: Vec<OsString>) {
170 if let Err(error) = run_service(arguments) {
171 println!("Error: {error}");
172 }
173 }
174
175 fn run_service(_arguments: Vec<OsString>) -> Result<(), windows_service::Error> {
176 let completed: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
177
178 let completed_event_handler = Arc::clone(&completed);
179 let event_handler = move |control_event| -> ServiceControlHandlerResult {
180 match control_event {
181 ServiceControl::Stop => {
182 completed_event_handler.store(true, Ordering::Relaxed);
183 // Handle stop event and return control back to the system.
184 ServiceControlHandlerResult::NoError
185 }
186 ServiceControl::Shutdown => {
187 completed_event_handler.store(true, Ordering::Relaxed);
188 // Handle stop event and return control back to the system.
189 ServiceControlHandlerResult::NoError
190 }
191 // ServiceControl::Preshutdown => {
192 // completed_event_handler.store(true, Ordering::Relaxed);
193 // ServiceControlHandlerResult::NoError
194 // }
195 // ServiceControl::PowerEvent(param) => {
196 // ServiceControlHandlerResult::NotImplemented
197 // }
198 // All services must accept Interrogate even if it's a no-op.
199 ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
200 _ => ServiceControlHandlerResult::NotImplemented,
201 }
202 };
203
204 // Register system service event handler
205 let status_handle = service_control_handler::register(consts::SERVICE_NAME, event_handler)?;
206
207 let running_status = ServiceStatus {
208 // Should match the one from system service registry
209 service_type: ServiceType::OWN_PROCESS,
210
211 // The new state
212 current_state: ServiceState::Running,
213
214 // Accept stop events when running
215 controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SHUTDOWN,
216
217 // Used to report an error when starting or stopping only, otherwise must be zero
218 exit_code: ServiceExitCode::Win32(0),
219
220 // Only used for pending states, otherwise must be zero
221 checkpoint: 0,
222
223 // Only used for pending states, otherwise must be zero
224 wait_hint: Duration::default(),
225
226 process_id: None,
227 };
228
229 status_handle.set_service_status(running_status)?;
230
231 main_loop::main_loop(completed.clone());
232
233 status_handle.set_service_status(ServiceStatus {
234 service_type: ServiceType::OWN_PROCESS,
235 current_state: ServiceState::Stopped,
236 controls_accepted: ServiceControlAccept::empty(),
237 exit_code: ServiceExitCode::Win32(0),
238 checkpoint: 0,
239 wait_hint: Duration::default(),
240 process_id: None,
241 })?;
242
243 info!("Main loop stopped: Temperature to RGB will now shut down");
244
245 Ok(())
246 }
247
248 fn tests() {
249 println!("Running some tests...");
250
251 // test_b650_e();
252 // list_usb_devices();
253 // test_roccat();
254 // test_wmi();
255 // test_corsair();
256 test_a770();
257 // test_read_temp();
258
259 println!("Press any key to continue...");
260 std::io::stdin().read_line(&mut String::new()).unwrap();
261 }
262
263 fn test_wmi() {
264 let com_con = COMLibrary::new().unwrap();
265 let wmi_con = WMIConnection::new(com_con.into()).unwrap();
266
267 //let results: Vec<HashMap<String, Variant>> = wmi_con.raw_query("SELECT * FROM Win32_PnPSignedDriver WHERE Description LIKE '%SMBUS%' OR Description LIKE '%SM BUS%'").unwrap();
268 //let results: Vec<HashMap<String, Variant>> = wmi_con.raw_query("SELECT * FROM Win32_PnPSignedDriver WHERE Description LIKE 'Intel(R) NF I2C Host Controller'").unwrap();
269 let results: Vec<HashMap<String, Variant>> = wmi_con
270 .raw_query("SELECT * FROM Win32_PnPSignedDriver")
271 .unwrap();
272 //let results: Vec<HashMap<String, Variant>> = wmi_con.raw_query("SELECT * FROM Win32_PnPAllocatedResource").unwrap();
273
274 for os in results {
275 println!("-------------------");
276 println!("{:#?}", os);
277 }
278 }
279 fn list_usb_devices() {
280 let api = hidapi::HidApi::new().unwrap();
281 for device in api.device_list() {
282 println!("{:?}", device);
283 println!("name: {}", device.product_string().unwrap());
284 println!("interface number: {}", device.interface_number());
285 println!("page: {}", device.usage_page());
286 println!("usage: {}", device.usage());
287 println!("----");
288 }
289 }
290
291 // fn test_roccat() {
292 // let api = hidapi::HidApi::new().unwrap();
293 // let roccat_device = roccat::get_device(&api);
294
295 // let manufacturer = roccat_device.get_manufacturer_string().unwrap();
296 // dbg!(manufacturer);
297
298 // let product = roccat_device.get_product_string().unwrap();
299 // dbg!(product);
300
301 // let serial = roccat_device.get_serial_number_string().unwrap();
302 // dbg!(serial);
303
304 // roccat::init(&roccat_device);
305 // roccat::set_color(
306 // &roccat_device,
307 // &RGB {
308 // red: 0,
309 // green: 255,
310 // blue: 40,
311 // },
312 // );
313 // }
314
315 fn test_b650_e() {
316 let api = hidapi::HidApi::new().unwrap();
317
318 let b650e_device = b650_e::get_device(&api);
319
320 println!("Firmware: {}", b650_e::get_firmware_string(&b650e_device));
321
322 let configuration = b650_e::get_configuration_table(&b650e_device);
323 println!("Configuration:");
324 for i in 0..60 {
325 print!("{:02X} ", configuration[i]);
326 if (i + 1) % 6 == 0 {
327 println!("");
328 }
329 }
330
331 // Only once, at start.
332 b650_e::set_fixed_mode(&b650e_device);
333
334 b650_e::set_color(
335 &b650e_device,
336 &RGB {
337 red: 255,
338 green: 0,
339 blue: 0,
340 },
341 );
342 b650_e::save_current_color(&b650e_device);
343 }
344
345 fn test_corsair() {
346 let corsair_controllers = [
347 corsair_vengeance::Controller::new(0x19),
348 corsair_vengeance::Controller::new(0x1B),
349 ];
350 for controller in corsair_controllers {
351 controller.set_color(&RGB {
352 red: 255,
353 green: 0,
354 blue: 0,
355 });
356 }
357 }
358
359 fn test_a770() {
360 // a770::set_rgb(255, 0, 0);
361 let mut a770 = a770::A770::new();
362 a770.set_color(255, 0, 0);
363 }
364
365 fn test_read_temp() {
366 let sensors = sensors_jiji::Sensors::new();
367 println!("temp cpu: {}", sensors.read_cpu_temp());
368 println!("temp gpu: {}", sensors.read_gpu_temp());
369 }