Skip to main content

xbp_cli/utils/
tray_runtime.rs

1//! Platform event pumping and tray icon loading for native system tray UIs.
2
3use tray_icon::Icon;
4
5const TRAY_ICON_SIZE: u32 = 32;
6
7/// Decode embedded PNG bytes and resize to a tray-friendly dimension.
8pub fn load_tray_icon_from_png(bytes: &[u8]) -> Result<Icon, String> {
9    let image = image::load_from_memory(bytes)
10        .map_err(|error| format!("Failed to decode tray icon: {error}"))?
11        .into_rgba8();
12
13    let image = if image.width() > TRAY_ICON_SIZE || image.height() > TRAY_ICON_SIZE {
14        image::imageops::resize(
15            &image,
16            TRAY_ICON_SIZE,
17            TRAY_ICON_SIZE,
18            image::imageops::FilterType::Lanczos3,
19        )
20    } else {
21        image
22    };
23
24    let (width, height) = (image.width(), image.height());
25    Icon::from_rgba(image.into_raw(), width, height)
26        .map_err(|error| format!("Failed to create tray icon: {error}"))
27}
28
29/// Pump native UI events so tray icons and context menus stay responsive.
30pub fn pump_tray_events() {
31    #[cfg(windows)]
32    pump_windows_events();
33
34    #[cfg(target_os = "linux")]
35    pump_gtk_events();
36}
37
38#[cfg(windows)]
39fn pump_windows_events() {
40    use std::mem::zeroed;
41    use windows_sys::Win32::UI::WindowsAndMessaging::{
42        DispatchMessageW, PeekMessageW, TranslateMessage, MSG, PM_REMOVE,
43    };
44
45    unsafe {
46        let mut msg: MSG = zeroed();
47        while PeekMessageW(&mut msg, std::ptr::null_mut(), 0, 0, PM_REMOVE) != 0 {
48            TranslateMessage(&msg);
49            DispatchMessageW(&msg);
50        }
51    }
52}
53
54#[cfg(target_os = "linux")]
55fn pump_gtk_events() {
56    while gtk::events_pending() {
57        gtk::main_iteration();
58    }
59}