1use crate::windows::memory::WindowsMemoryOptimizer;
4use crate::accel::CpuCapabilities;
5use std::sync::{Arc, atomic::{AtomicBool, AtomicU32, Ordering}};
6use tray_icon::{
7 menu::{Menu, MenuEvent, MenuItem, CheckMenuItem, PredefinedMenuItem},
8 TrayIconBuilder, Icon,
9};
10use winit::event_loop::{ControlFlow, EventLoop};
11
12const AUTO_OPTIMIZE_THRESHOLD: u32 = 75;
14const AUTO_OPTIMIZE_INTERVAL: u64 = 60;
16
17pub struct TrayApp {
18 running: Arc<AtomicBool>,
19}
20
21impl TrayApp {
22 pub fn new() -> Self {
23 Self {
24 running: Arc::new(AtomicBool::new(true)),
25 }
26 }
27
28 pub fn run(&self) -> Result<(), Box<dyn std::error::Error>> {
29 let event_loop = EventLoop::new()?;
30
31 let status_text = get_memory_status_text();
33
34 let menu = Menu::new();
36 let status_item = MenuItem::new(&status_text, false, None);
37 let auto_item = CheckMenuItem::new("Auto-Optimize (60s)", true, true, None);
38 let optimize_item = MenuItem::new("Optimize Now", true, None);
39 let aggressive_item = MenuItem::new("Deep Clean", true, None);
40 let cpu_item = MenuItem::new("System Info", true, None);
41 let quit_item = MenuItem::new("Quit", true, None);
42
43 menu.append(&status_item)?;
44 menu.append(&PredefinedMenuItem::separator())?;
45 menu.append(&auto_item)?;
46 menu.append(&optimize_item)?;
47 menu.append(&aggressive_item)?;
48 menu.append(&PredefinedMenuItem::separator())?;
49 menu.append(&cpu_item)?;
50 menu.append(&PredefinedMenuItem::separator())?;
51 menu.append(&quit_item)?;
52
53 let initial_usage = WindowsMemoryOptimizer::get_memory_status()
55 .map(|s| s.memory_load_percent)
56 .unwrap_or(50);
57
58 let icon_data = create_icon_with_usage(initial_usage);
60 let icon = Icon::from_rgba(icon_data, 32, 32)?;
61
62 let tray_icon = TrayIconBuilder::new()
63 .with_menu(Box::new(menu))
64 .with_tooltip("RuVector Memory Optimizer - Auto-optimizing")
65 .with_icon(icon)
66 .build()?;
67
68 let optimize_id = optimize_item.id().clone();
69 let aggressive_id = aggressive_item.id().clone();
70 let cpu_id = cpu_item.id().clone();
71 let quit_id = quit_item.id().clone();
72 let auto_id = auto_item.id().clone();
73
74 let running = self.running.clone();
75 let mut last_update = std::time::Instant::now();
76 let mut last_auto_optimize = std::time::Instant::now();
77 let auto_enabled = Arc::new(AtomicBool::new(true));
78 let last_usage = Arc::new(AtomicU32::new(initial_usage));
79 let total_freed = Arc::new(AtomicU32::new(0));
80
81 #[allow(deprecated)]
83 event_loop.run(move |_event, event_loop| {
84 event_loop.set_control_flow(ControlFlow::WaitUntil(
85 std::time::Instant::now() + std::time::Duration::from_secs(1)
86 ));
87
88 if last_update.elapsed() > std::time::Duration::from_secs(5) {
90 if let Ok(status) = WindowsMemoryOptimizer::get_memory_status() {
91 let usage = status.memory_load_percent;
92 last_usage.store(usage, Ordering::SeqCst);
93
94 let freed = total_freed.load(Ordering::SeqCst);
96 let text = if freed > 0 {
97 format!(
98 "Memory: {:.0}% ({:.1}/{:.1} GB) | Freed: {} MB",
99 usage,
100 status.used_physical_mb() / 1024.0,
101 status.total_physical_mb / 1024.0,
102 freed
103 )
104 } else {
105 format!(
106 "Memory: {:.0}% ({:.1}/{:.1} GB)",
107 usage,
108 status.used_physical_mb() / 1024.0,
109 status.total_physical_mb / 1024.0
110 )
111 };
112 let _ = status_item.set_text(&text);
113
114 let icon_data = create_icon_with_usage(usage);
116 if let Ok(new_icon) = Icon::from_rgba(icon_data, 32, 32) {
117 let _ = tray_icon.set_icon(Some(new_icon));
118 }
119
120 let tooltip = if auto_enabled.load(Ordering::SeqCst) {
122 format!("RuVector MemOpt - {}% | Auto-optimizing", usage)
123 } else {
124 format!("RuVector MemOpt - {}% | Manual mode", usage)
125 };
126 let _ = tray_icon.set_tooltip(Some(tooltip));
127
128 if auto_enabled.load(Ordering::SeqCst)
130 && usage > AUTO_OPTIMIZE_THRESHOLD
131 && last_auto_optimize.elapsed() > std::time::Duration::from_secs(AUTO_OPTIMIZE_INTERVAL)
132 {
133 let total_freed_clone = total_freed.clone();
134 std::thread::spawn(move || {
135 let optimizer = WindowsMemoryOptimizer::new();
136 if let Ok(result) = optimizer.optimize(false) {
137 if result.freed_mb > 100.0 {
138 let current = total_freed_clone.load(Ordering::SeqCst);
140 total_freed_clone.store(current + result.freed_mb as u32, Ordering::SeqCst);
141 }
142 }
143 });
144 last_auto_optimize = std::time::Instant::now();
145 }
146 }
147 last_update = std::time::Instant::now();
148 }
149
150 if let Ok(event) = MenuEvent::receiver().try_recv() {
152 if event.id == quit_id {
153 running.store(false, Ordering::SeqCst);
154 event_loop.exit();
155 } else if event.id == optimize_id {
156 let total_freed_clone = total_freed.clone();
157 run_optimization(false, total_freed_clone);
158 } else if event.id == aggressive_id {
159 let total_freed_clone = total_freed.clone();
160 run_optimization(true, total_freed_clone);
161 } else if event.id == cpu_id {
162 show_cpu_info();
163 } else if event.id == auto_id {
164 let current = auto_enabled.load(Ordering::SeqCst);
165 auto_enabled.store(!current, Ordering::SeqCst);
166 let _ = auto_item.set_checked(!current);
167 }
168 }
169 })?;
170
171 Ok(())
172 }
173}
174
175fn get_memory_status_text() -> String {
176 if let Ok(status) = WindowsMemoryOptimizer::get_memory_status() {
177 format!(
178 "Memory: {:.0}% ({:.1}/{:.1} GB)",
179 status.memory_load_percent,
180 status.used_physical_mb() / 1024.0,
181 status.total_physical_mb / 1024.0
182 )
183 } else {
184 "Memory: Unknown".to_string()
185 }
186}
187
188fn run_optimization(aggressive: bool, total_freed: Arc<AtomicU32>) {
189 std::thread::spawn(move || {
190 let optimizer = WindowsMemoryOptimizer::new();
191 match optimizer.optimize(aggressive) {
192 Ok(result) => {
193 let current = total_freed.load(Ordering::SeqCst);
194 total_freed.store(current + result.freed_mb as u32, Ordering::SeqCst);
195
196 let msg = format!(
197 "Optimization Complete!\n\nFreed: {:.1} MB\nProcesses: {}\nTime: {} ms",
198 result.freed_mb, result.processes_trimmed, result.duration_ms
199 );
200 show_message_box("RuVector Optimizer", &msg);
201 }
202 Err(e) => {
203 show_message_box("RuVector Optimizer", &format!("Error: {}", e));
204 }
205 }
206 });
207}
208
209fn show_cpu_info() {
210 let caps = CpuCapabilities::detect();
211 let msg = format!(
212 "CPU: {}\n\nCores: {}\nAVX2: {}\nAVX-512: {}\nAVX-VNNI: {}\nIntel NPU: {}\n\nEstimated SIMD Speedup: {:.0}x",
213 caps.model,
214 caps.core_count,
215 if caps.has_avx2 { "Yes" } else { "No" },
216 if caps.has_avx512 { "Yes" } else { "No" },
217 if caps.has_avx_vnni { "Yes" } else { "No" },
218 if caps.has_npu { "Yes" } else { "No" },
219 caps.estimated_speedup()
220 );
221 show_message_box("CPU Information", &msg);
222}
223
224fn show_message_box(title: &str, message: &str) {
225 #[cfg(windows)]
226 {
227 use std::ffi::OsStr;
228 use std::os::windows::ffi::OsStrExt;
229 use std::ptr;
230
231 fn to_wide(s: &str) -> Vec<u16> {
232 OsStr::new(s).encode_wide().chain(Some(0)).collect()
233 }
234
235 let title = to_wide(title);
236 let message = to_wide(message);
237
238 unsafe {
239 windows::Win32::UI::WindowsAndMessaging::MessageBoxW(
240 windows::Win32::Foundation::HWND(ptr::null_mut()),
241 windows::core::PCWSTR(message.as_ptr()),
242 windows::core::PCWSTR(title.as_ptr()),
243 windows::Win32::UI::WindowsAndMessaging::MB_OK |
244 windows::Win32::UI::WindowsAndMessaging::MB_ICONINFORMATION,
245 );
246 }
247 }
248}
249
250fn create_icon_with_usage(usage_percent: u32) -> Vec<u8> {
252 let mut data = Vec::with_capacity(32 * 32 * 4);
253
254 let (r, g, b) = if usage_percent < 60 {
256 (0x00u8, 0xC8u8, 0x50u8) } else if usage_percent < 80 {
258 (0xFFu8, 0xA5u8, 0x00u8) } else {
260 (0xE0u8, 0x30u8, 0x30u8) };
262
263 let (border_r, border_g, border_b) = if usage_percent < 60 {
264 (0x00u8, 0x80u8, 0x30u8)
265 } else if usage_percent < 80 {
266 (0xCCu8, 0x80u8, 0x00u8)
267 } else {
268 (0xA0u8, 0x20u8, 0x20u8)
269 };
270
271 for y in 0..32 {
272 for x in 0..32 {
273 let in_body = x >= 4 && x < 28 && y >= 2 && y < 30;
275 let in_notch = x >= 12 && x < 20 && y < 4;
276 let in_chip = in_body && !in_notch;
277
278 let left_pin = x < 4 && (y == 8 || y == 14 || y == 20 || y == 26);
280 let right_pin = x >= 28 && (y == 8 || y == 14 || y == 20 || y == 26);
281 let is_pin = left_pin || right_pin;
282
283 let is_border = in_chip && (x == 4 || x == 27 || y == 2 || y == 29 ||
285 (y == 3 && (x < 12 || x >= 20)));
286
287 let fill_height = 28 - ((usage_percent as i32 * 26) / 100);
289 let is_filled = in_chip && !is_border && (y as i32) >= fill_height;
290
291 if is_pin {
292 data.push(border_r);
294 data.push(border_g);
295 data.push(border_b);
296 data.push(0xFF);
297 } else if is_border {
298 data.push(border_r);
300 data.push(border_g);
301 data.push(border_b);
302 data.push(0xFF);
303 } else if is_filled {
304 data.push(r);
306 data.push(g);
307 data.push(b);
308 data.push(0xFF);
309 } else if in_chip {
310 data.push(r / 3);
312 data.push(g / 3);
313 data.push(b / 3);
314 data.push(0xFF);
315 } else {
316 data.push(0x00);
318 data.push(0x00);
319 data.push(0x00);
320 data.push(0x00);
321 }
322 }
323 }
324
325 data
326}
327
328impl Default for TrayApp {
329 fn default() -> Self {
330 Self::new()
331 }
332}