diff --git a/native/libwinbridge/bridge.cpp b/native/libwinbridge/bridge.cpp index 01024c1..4aa279f 100644 --- a/native/libwinbridge/bridge.cpp +++ b/native/libwinbridge/bridge.cpp @@ -62,6 +62,7 @@ HWND nw = NULL; HWND hwnd_st_u = NULL; HBITMAP g_espanso_bmp = NULL; HICON g_espanso_ico = NULL; +HICON g_espanso_red_ico = NULL; NOTIFYICONDATA nid = {}; UINT WM_TASKBARCREATED = RegisterWindowMessage(L"TaskbarCreated"); @@ -309,13 +310,14 @@ LRESULT CALLBACK window_procedure(HWND window, unsigned int msg, WPARAM wp, LPAR } } -int32_t initialize(void * self, wchar_t * ico_path, wchar_t * bmp_path, int32_t _show_icon) { +int32_t initialize(void * self, wchar_t * ico_path, wchar_t * red_ico_path, wchar_t * bmp_path, int32_t _show_icon) { manager_instance = self; show_icon = _show_icon; // Load the images g_espanso_bmp = (HBITMAP)LoadImage(NULL, bmp_path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); g_espanso_ico = (HICON)LoadImage(NULL, ico_path, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_SHARED | LR_DEFAULTSIZE | LR_LOADFROMFILE); + g_espanso_red_ico = (HICON)LoadImage(NULL, red_ico_path, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR | LR_SHARED | LR_DEFAULTSIZE | LR_LOADFROMFILE); // Make the notification capable of handling different screen definitions SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); @@ -472,6 +474,19 @@ void eventloop() { // Something went wrong, this should have been an infinite loop. } +void update_tray_icon(int32_t enabled) { + if (enabled) { + nid.hIcon = g_espanso_ico; + }else{ + nid.hIcon = g_espanso_red_ico; + } + + // Update the icon + if (show_icon) { + Shell_NotifyIcon(NIM_MODIFY, &nid); + } +} + /* * Type the given string simulating keyboard presses. */ diff --git a/native/libwinbridge/bridge.h b/native/libwinbridge/bridge.h index f0ef9eb..7db802a 100644 --- a/native/libwinbridge/bridge.h +++ b/native/libwinbridge/bridge.h @@ -33,7 +33,7 @@ extern void * manager_instance; * Initialize the Windows parameters * return: 1 if OK, -1 otherwise. */ -extern "C" int32_t initialize(void * self, wchar_t * ico_path, wchar_t * bmp_path, int32_t show_icon); +extern "C" int32_t initialize(void * self, wchar_t * ico_path, wchar_t * red_ico_path, wchar_t * bmp_path, int32_t show_icon); #define LEFT_VARIANT 1 #define RIGHT_VARIANT 2 @@ -152,6 +152,11 @@ extern "C" int32_t show_notification(wchar_t * message); */ extern "C" void close_notification(); +/* + * Update the tray icon status + */ +extern "C" void update_tray_icon(int32_t enabled); + // CLIPBOARD /* diff --git a/src/bridge/windows.rs b/src/bridge/windows.rs index 35cec74..dd7ed32 100644 --- a/src/bridge/windows.rs +++ b/src/bridge/windows.rs @@ -33,6 +33,7 @@ extern "C" { pub fn initialize( s: *const c_void, ico_path: *const u16, + red_ico_path: *const u16, bmp_path: *const u16, show_icon: i32, ) -> i32; @@ -48,6 +49,7 @@ extern "C" { pub fn register_icon_click_callback(cb: extern "C" fn(_self: *mut c_void)); pub fn register_context_menu_click_callback(cb: extern "C" fn(_self: *mut c_void, id: i32)); pub fn cleanup_ui(); + pub fn update_tray_icon(enabled: i32); // CLIPBOARD pub fn get_clipboard(buffer: *mut u16, size: i32) -> i32; diff --git a/src/context/mod.rs b/src/context/mod.rs index 50805fb..cd3a5aa 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -48,6 +48,11 @@ pub fn new( macos::MacContext::new(config, send_channel, is_injecting) } +#[cfg(target_os = "macos")] +pub fn update_icon(enabled: bool) { + // TODO: add update icon on macOS +} + // LINUX IMPLEMENTATION #[cfg(target_os = "linux")] pub fn new( @@ -58,6 +63,11 @@ pub fn new( linux::LinuxContext::new(config, send_channel, is_injecting) } +#[cfg(target_os = "linux")] +pub fn update_icon(enabled: bool) { + // No icon on Linux +} + // WINDOWS IMPLEMENTATION #[cfg(target_os = "windows")] pub fn new( @@ -68,6 +78,11 @@ pub fn new( windows::WindowsContext::new(config, send_channel, is_injecting) } +#[cfg(target_os = "windows")] +pub fn update_icon(enabled: bool) { + windows::update_icon(enabled); +} + // espanso directories static WARING_INIT: Once = Once::new(); diff --git a/src/context/windows.rs b/src/context/windows.rs index 07eeffa..e1cf95a 100644 --- a/src/context/windows.rs +++ b/src/context/windows.rs @@ -32,6 +32,7 @@ use widestring::{U16CStr, U16CString}; const BMP_BINARY: &[u8] = include_bytes!("../res/win/espanso.bmp"); const ICO_BINARY: &[u8] = include_bytes!("../res/win/espanso.ico"); +const RED_ICO_BINARY: &[u8] = include_bytes!("../res/win/espansored.ico"); pub struct WindowsContext { send_channel: Sender, @@ -77,8 +78,22 @@ impl WindowsContext { ); } + let espanso_red_ico_image = espanso_dir.join("espansored.ico"); + if espanso_red_ico_image.exists() { + info!("red ICO already initialized, skipping."); + } else { + fs::write(&espanso_red_ico_image, RED_ICO_BINARY) + .expect("Unable to write windows ico file"); + + info!( + "Extracted 'red ico' icon to: {}", + espanso_red_ico_image.to_str().unwrap_or("error") + ); + } + let bmp_icon = espanso_bmp_image.to_str().unwrap_or_default(); let ico_icon = espanso_ico_image.to_str().unwrap_or_default(); + let red_ico_icon = espanso_red_ico_image.to_str().unwrap_or_default(); let send_channel = send_channel; @@ -96,6 +111,7 @@ impl WindowsContext { register_context_menu_click_callback(context_menu_click_callback); let ico_file_c = U16CString::from_str(ico_icon).unwrap(); + let red_ico_file_c = U16CString::from_str(red_ico_icon).unwrap(); let bmp_file_c = U16CString::from_str(bmp_icon).unwrap(); let show_icon = if config.show_icon { 1 } else { 0 }; @@ -104,6 +120,7 @@ impl WindowsContext { let res = initialize( context_ptr, ico_file_c.as_ptr(), + red_ico_file_c.as_ptr(), bmp_file_c.as_ptr(), show_icon, ); @@ -126,6 +143,12 @@ impl super::Context for WindowsContext { // Native bridge code +pub fn update_icon(enabled: bool) { + unsafe { + crate::bridge::windows::update_tray_icon(if enabled { 1 } else { 0 }); + } +} + extern "C" fn keypress_callback( _self: *mut c_void, raw_buffer: *const u16, diff --git a/src/engine.rs b/src/engine.rs index 2042fcd..3e2a534 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -377,6 +377,9 @@ impl< if config.show_notifications { self.ui_manager.notify(message); } + + // Update the icon on supported OSes. + crate::context::update_icon(status); } fn on_passive(&self) { diff --git a/src/res/win/espansored.ico b/src/res/win/espansored.ico new file mode 100644 index 0000000..334bab0 Binary files /dev/null and b/src/res/win/espansored.ico differ