1use crate::error::ControllerError;
4use crate::modes::{DisplayMode, EReadingMode, EyeCareMode, ManualMode, NormalMode, VividMode};
5use crate::state::ControllerState;
6
7use libloading::{Library, Symbol};
8use log::{debug, info};
9use std::ffi::c_void;
10use std::fs;
11use std::sync::atomic::{AtomicBool, Ordering};
12use windows_sys::Win32::{
13 Foundation::ERROR_INSUFFICIENT_BUFFER,
14 Storage::Packaging::Appx::{
15 FindPackagesByPackageFamily, GetPackagePathByFullName, PACKAGE_FILTER_HEAD,
16 },
17};
18
19const LOCAL_DLL_NAME: &str = "AsusCustomizationRpcClient.dll";
20
21pub trait DisplayController: Send + Sync {
29 fn get_state(&self) -> ControllerState;
31
32 fn refresh_sliders(&self) -> Result<(), ControllerError>;
34
35 fn sync_all_sliders(&self) -> Result<(), ControllerError>;
37
38 fn set_dimming(&self, level: i32) -> Result<(), ControllerError>;
40
41 fn set_dimming_percent(&self, percent: i32) -> Result<(), ControllerError>;
43
44 fn get_current_mode(&self) -> Result<Box<dyn DisplayMode>, ControllerError>;
46
47 fn set_mode(&self, mode: &dyn DisplayMode) -> Result<(), ControllerError>;
49
50 fn toggle_e_reading(&self) -> Result<Box<dyn DisplayMode>, ControllerError>;
52}
53
54mod callback_state {
59 use super::ControllerState;
60 use log::{debug, trace};
61 use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
62
63 static CURRENT_MODE: AtomicI32 = AtomicI32::new(-1);
64 static IS_MONOCHROME: AtomicBool = AtomicBool::new(false);
65 static LAST_NON_EREADING_MODE: AtomicI32 = AtomicI32::new(1);
66
67 static MANUAL_SLIDER: AtomicI32 = AtomicI32::new(50);
68 static EYECARE_SLIDER: AtomicI32 = AtomicI32::new(2);
69 static EREADING_GRAYSCALE: AtomicI32 = AtomicI32::new(3);
70 static EREADING_TEMP: AtomicI32 = AtomicI32::new(562);
71 static CURRENT_DIMMING: AtomicI32 = AtomicI32::new(-1);
72
73 pub(super) fn snapshot() -> ControllerState {
74 ControllerState {
75 mode_id: CURRENT_MODE.load(Ordering::SeqCst),
76 is_monochrome: IS_MONOCHROME.load(Ordering::SeqCst),
77 dimming: CURRENT_DIMMING.load(Ordering::SeqCst),
78 manual_slider: MANUAL_SLIDER.load(Ordering::SeqCst) as u8,
79 eyecare_level: EYECARE_SLIDER.load(Ordering::SeqCst) as u8,
80 ereading_grayscale: EREADING_GRAYSCALE.load(Ordering::SeqCst) as u8,
81 ereading_temp: EREADING_TEMP.load(Ordering::SeqCst) as u8,
82 last_non_ereading_mode: LAST_NON_EREADING_MODE.load(Ordering::SeqCst),
83 }
84 }
85
86 pub(super) fn store_last_non_ereading_mode(mode_id: i32) {
87 LAST_NON_EREADING_MODE.store(mode_id, Ordering::SeqCst);
88 }
89
90 pub(super) fn store_dimming(value: i32) {
91 CURRENT_DIMMING.store(value, Ordering::SeqCst);
92 }
93
94 pub(super) extern "C" fn mode_callback(func: i32, data: i32, str_data: *const i8) {
95 let s = if str_data.is_null() {
96 String::from("null")
97 } else {
98 unsafe {
99 std::ffi::CStr::from_ptr(str_data)
100 .to_string_lossy()
101 .to_string()
102 }
103 };
104
105 trace!("callback: func={}, data={}, str='{}'", func, data, s);
106
107 match func {
108 18 => {
109 let parts: Vec<&str> = s.split(',').collect();
110 if parts.len() >= 2 {
111 if let Ok(dimming) = parts[1].parse::<i32>() {
112 CURRENT_DIMMING.store(dimming, Ordering::SeqCst);
113 }
114 }
115 if parts.len() >= 3 {
116 if let Ok(mono) = parts[2].parse::<i32>() {
117 IS_MONOCHROME.store(mono != 0, Ordering::SeqCst);
118 }
119 }
120 CURRENT_MODE.store(data, Ordering::SeqCst);
121
122 debug!(
123 "mode updated: data={}, dimming={}, monochrome={}",
124 data,
125 CURRENT_DIMMING.load(Ordering::SeqCst),
126 IS_MONOCHROME.load(Ordering::SeqCst)
127 );
128 }
129 20 => {
130 MANUAL_SLIDER.store(data, Ordering::SeqCst);
131 debug!("manual slider updated: {}", data);
132 }
133 21 => {
134 EYECARE_SLIDER.store(data, Ordering::SeqCst);
135 debug!("eyecare slider updated: {}", data);
136 }
137 27 => {
138 let raw = data + 206;
139 let grayscale = raw / 256;
140 let temp = raw % 256;
141 EREADING_GRAYSCALE.store(grayscale, Ordering::SeqCst);
142 EREADING_TEMP.store(temp, Ordering::SeqCst);
143 debug!("e-reading updated: grayscale={}, temp={}", grayscale, temp);
144 }
145 _ => {}
146 }
147 }
148}
149
150static INSTANCE_EXISTS: AtomicBool = AtomicBool::new(false);
156
157pub struct AsusController {
178 lib: Library,
179 client: *mut c_void,
180}
181
182unsafe impl Send for AsusController {}
185unsafe impl Sync for AsusController {}
186
187impl AsusController {
188 pub fn new() -> Result<Self, ControllerError> {
200 if INSTANCE_EXISTS.swap(true, Ordering::SeqCst) {
201 return Err(ControllerError::AlreadyInitialized);
202 }
203
204 match Self::init_internal() {
205 Ok(controller) => Ok(controller),
206 Err(e) => {
207 INSTANCE_EXISTS.store(false, Ordering::SeqCst);
208 Err(e)
209 }
210 }
211 }
212
213 fn init_internal() -> Result<Self, ControllerError> {
214 let full_name = find_asus_package()?;
215 let path = get_package_path(&full_name)?;
216 let dll_path = format!("{}\\ModuleDll\\HWSettings\\{}", path, LOCAL_DLL_NAME);
217
218 fs::copy(&dll_path, LOCAL_DLL_NAME)?;
219
220 unsafe {
221 let lib = Library::new(LOCAL_DLL_NAME)?;
222
223 type InitFn = unsafe extern "C" fn(*mut *mut c_void) -> i64;
224 let init: Symbol<InitFn> = lib.get(b"MyOptRpcClientInitialize")?;
225
226 let mut client: *mut c_void = std::ptr::null_mut();
227 let result = init(&mut client);
228 if result != 0 || client.is_null() {
229 return Err(ControllerError::RpcInitFailed);
230 }
231
232 type CallbackFn = unsafe extern "C" fn(i32, i32, *const i8);
233 type SetCallbackFn = unsafe extern "C" fn(CallbackFn, *mut c_void);
234 let set_callback: Symbol<SetCallbackFn> =
235 lib.get(b"SetCallbackForReturnOptimizationResult")?;
236 set_callback(callback_state::mode_callback, client);
237
238 Ok(Self { lib, client })
239 }
240 }
241
242 fn call_rpc_get(&self, symbol: &[u8]) -> Result<i64, ControllerError> {
243 unsafe {
244 type GetFn = unsafe extern "C" fn(*mut c_void) -> i64;
245 let func: Symbol<GetFn> = self.lib.get(symbol)?;
246 Ok(func(self.client))
247 }
248 }
249
250 pub fn set_splendid_mode(&self, symbol: &[u8], value: u8) -> Result<(), ControllerError> {
254 unsafe {
255 type SetModeFn = unsafe extern "C" fn(u8, *const i8, *mut c_void) -> i64;
256 let set_fn: Symbol<SetModeFn> = self.lib.get(symbol)?;
257 let empty_str = b"\0".as_ptr() as *const i8;
258 set_fn(value, empty_str, self.client);
259 Ok(())
260 }
261 }
262
263 pub fn set_monochrome_mode(&self, grayscale: u8, temp: u8) -> Result<(), ControllerError> {
267 unsafe {
268 type SetMonoFn = unsafe extern "C" fn(i32, *mut c_void) -> i64;
269 let set_mono: Symbol<SetMonoFn> = self.lib.get(b"MyOptSetSplendidMonochromeFunc")?;
270 let value = (grayscale as i32 * 256) + temp as i32 - 206;
271 set_mono(value, self.client);
272 Ok(())
273 }
274 }
275
276 pub fn dimming_to_percent(splendid_value: i32) -> i32 {
278 let clamped = splendid_value.clamp(40, 100);
279 ((clamped - 40) as f32 / 60.0 * 100.0).round() as i32
280 }
281
282 pub fn percent_to_dimming(percent: i32) -> i32 {
284 40 + (percent as f32 / 100.0 * 60.0).round() as i32
285 }
286
287 fn mode_from_state(
288 &self,
289 state: &ControllerState,
290 ) -> Result<Box<dyn DisplayMode>, ControllerError> {
291 match (state.mode_id, state.is_monochrome) {
292 (1, false) => Ok(Box::new(NormalMode::new())),
293 (2, false) => Ok(Box::new(VividMode::new())),
294 (6, false) => Ok(Box::new(ManualMode::from_controller_state(state))),
295 (7, false) => Ok(Box::new(EyeCareMode::from_controller_state(state))),
296 (_, true) => {
297 callback_state::store_last_non_ereading_mode(state.mode_id);
298 Ok(Box::new(EReadingMode::from_controller_state(state)))
299 }
300 _ => Err(ControllerError::ModeNotDetected),
301 }
302 }
303
304 fn restore_last_mode(&self, state: &ControllerState) -> Box<dyn DisplayMode> {
305 match state.last_non_ereading_mode {
306 2 => Box::new(VividMode::new()),
307 6 => Box::new(ManualMode::from_controller_state(state)),
308 7 => Box::new(EyeCareMode::from_controller_state(state)),
309 _ => Box::new(NormalMode::new()),
310 }
311 }
312}
313
314impl DisplayController for AsusController {
315 fn get_state(&self) -> ControllerState {
316 callback_state::snapshot()
317 }
318
319 fn refresh_sliders(&self) -> Result<(), ControllerError> {
320 self.call_rpc_get(b"MyOptGetSplendidManualModeFunc")?;
321 self.call_rpc_get(b"MyOptGetSplendidEyecareModeFunc")?;
322 self.call_rpc_get(b"MyOptGetSplendidMonochromeFunc")?;
323 Ok(())
324 }
325
326 fn sync_all_sliders(&self) -> Result<(), ControllerError> {
327 debug!("syncing all sliders from ASUS...");
328
329 let _ = self.get_current_mode();
330 self.refresh_sliders()?;
331 std::thread::sleep(std::time::Duration::from_millis(500));
332
333 let state = self.get_state();
334 debug!(
335 "sync complete: dimming={}({}%), manual={}, eyecare={}, e-reading(grayscale={}, temp={})",
336 state.dimming,
337 Self::dimming_to_percent(state.dimming),
338 state.manual_slider,
339 state.eyecare_level,
340 state.ereading_grayscale,
341 state.ereading_temp
342 );
343 Ok(())
344 }
345
346 fn set_dimming(&self, level: i32) -> Result<(), ControllerError> {
347 let level = level.clamp(40, 100);
348 unsafe {
349 type SetDimmingFn = unsafe extern "C" fn(i32, *const i8, *mut c_void) -> i64;
350 let set_dimming: Symbol<SetDimmingFn> = self.lib.get(b"MyOptSetSplendidDimmingFunc")?;
351
352 let empty_str = b"\0".as_ptr() as *const i8;
353 let result = set_dimming(level, empty_str, self.client);
354 debug!("set dimming to {}, result: {}", level, result);
355
356 if result == 0 {
357 callback_state::store_dimming(level);
358 Ok(())
359 } else {
360 Err(ControllerError::DimmingFailed(result))
361 }
362 }
363 }
364
365 fn set_dimming_percent(&self, percent: i32) -> Result<(), ControllerError> {
366 let splendid_value = Self::percent_to_dimming(percent.clamp(0, 100));
367 self.set_dimming(splendid_value)
368 }
369
370 fn get_current_mode(&self) -> Result<Box<dyn DisplayMode>, ControllerError> {
371 self.call_rpc_get(b"MyOptGetSplendidColorModeFunc")?;
372 std::thread::sleep(std::time::Duration::from_millis(500));
373
374 let state = self.get_state();
375 self.mode_from_state(&state)
376 }
377
378 fn set_mode(&self, mode: &dyn DisplayMode) -> Result<(), ControllerError> {
379 mode.apply(self)
380 }
381
382 fn toggle_e_reading(&self) -> Result<Box<dyn DisplayMode>, ControllerError> {
383 let current = self.get_current_mode()?;
384 debug!("current mode: {:?}", current);
385
386 let state = self.get_state();
387 let target: Box<dyn DisplayMode> = if current.is_ereading() {
388 let restored = self.restore_last_mode(&state);
389 info!("switching from e-reading to {:?}", restored);
390 restored
391 } else {
392 info!("switching to e-reading");
393 Box::new(EReadingMode::from_controller_state(&state))
394 };
395
396 self.set_mode(&*target)?;
397 Ok(target)
398 }
399}
400
401impl Drop for AsusController {
402 fn drop(&mut self) {
403 unsafe {
404 type UninitFn = unsafe extern "C" fn(*mut c_void);
405 if let Ok(uninit) = self.lib.get::<UninitFn>(b"MyOptRpcClientUninitialize") {
406 uninit(self.client);
407 }
408 }
409 INSTANCE_EXISTS.store(false, Ordering::SeqCst);
410 }
411}
412
413fn find_asus_package() -> Result<String, ControllerError> {
418 let family_name: Vec<u16> = "B9ECED6F.ASUSPCAssistant_qmba6cd70vzyy\0"
419 .encode_utf16()
420 .collect();
421
422 let mut count = 0u32;
423 let mut buffer_length = 0u32;
424
425 let result = unsafe {
426 FindPackagesByPackageFamily(
427 family_name.as_ptr(),
428 PACKAGE_FILTER_HEAD,
429 &mut count,
430 std::ptr::null_mut(),
431 &mut buffer_length,
432 std::ptr::null_mut(),
433 std::ptr::null_mut(),
434 )
435 };
436
437 if result != ERROR_INSUFFICIENT_BUFFER {
438 return Err(ControllerError::PackageNotFound(result));
439 }
440
441 let mut package_names: Vec<*mut u16> = vec![std::ptr::null_mut(); count as usize];
442 let mut buffer: Vec<u16> = vec![0; buffer_length as usize];
443
444 let result = unsafe {
445 FindPackagesByPackageFamily(
446 family_name.as_ptr(),
447 PACKAGE_FILTER_HEAD,
448 &mut count,
449 package_names.as_mut_ptr(),
450 &mut buffer_length,
451 buffer.as_mut_ptr(),
452 std::ptr::null_mut(),
453 )
454 };
455
456 if result != 0 {
457 return Err(ControllerError::PackageNotFound(result));
458 }
459
460 let full_name = unsafe {
461 let ptr = package_names[0];
462 let len = (0..).take_while(|&i| *ptr.add(i) != 0).count();
463 String::from_utf16_lossy(std::slice::from_raw_parts(ptr, len))
464 };
465
466 Ok(full_name)
467}
468
469fn get_package_path(full_name: &str) -> Result<String, ControllerError> {
470 let full_name_wide: Vec<u16> = format!("{}\0", full_name).encode_utf16().collect();
471 let mut buffer_length = 0u32;
472
473 let result = unsafe {
474 GetPackagePathByFullName(
475 full_name_wide.as_ptr(),
476 &mut buffer_length,
477 std::ptr::null_mut(),
478 )
479 };
480
481 if result != ERROR_INSUFFICIENT_BUFFER {
482 return Err(ControllerError::PackagePathError(result));
483 }
484
485 let mut buffer: Vec<u16> = vec![0; buffer_length as usize];
486 let result = unsafe {
487 GetPackagePathByFullName(
488 full_name_wide.as_ptr(),
489 &mut buffer_length,
490 buffer.as_mut_ptr(),
491 )
492 };
493
494 if result != 0 {
495 return Err(ControllerError::PackagePathError(result));
496 }
497
498 let len = buffer.iter().take_while(|&&c| c != 0).count();
499 Ok(String::from_utf16_lossy(&buffer[..len]))
500}