diff --git a/native/libwinbridge/bridge.cpp b/native/libwinbridge/bridge.cpp index 316ff29..0f6bf53 100644 --- a/native/libwinbridge/bridge.cpp +++ b/native/libwinbridge/bridge.cpp @@ -277,7 +277,8 @@ int32_t get_active_window_executable(wchar_t * buffer, int32_t size) { // UI const wchar_t* const notification_winclass = L"EspansoNotification"; -HWND nw; +HWND nw = NULL; +HWND hwnd_st_u = NULL; HBITMAP g_espanso_icon = NULL; /* @@ -321,7 +322,8 @@ LRESULT CALLBACK notification_worker_procedure(HWND window, unsigned int msg, WP hdcStatic = (HDC)wp; SetTextColor(hdcStatic, RGB(0, 0, 0)); - SetBkMode(hdcStatic, TRANSPARENT); + SetBkColor(hdcStatic, RGB(255, 255, 255)); + //SetBkMode(hdcStatic, OPAQUE); return (LRESULT)GetStockObject(NULL_BRUSH); default: @@ -330,6 +332,11 @@ LRESULT CALLBACK notification_worker_procedure(HWND window, unsigned int msg, WP } int32_t show_notification(wchar_t * message, wchar_t * icon_path) { + if (nw != NULL) { + SetWindowText(hwnd_st_u, L" "); + SetWindowText(hwnd_st_u, message); + } + g_espanso_icon = (HBITMAP)LoadImage(NULL, icon_path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); @@ -370,8 +377,6 @@ int32_t show_notification(wchar_t * message, wchar_t * icon_path) { if (nw) { - - static HWND hwnd_st_u, hwnd_ed_u; int x, w, y, h; y = 40; h = 30; x = 100; w = 180; @@ -386,7 +391,7 @@ int32_t show_notification(wchar_t * message, wchar_t * icon_path) { int posX = GetSystemMetrics(SM_CXSCREEN) - 350; int posY = GetSystemMetrics(SM_CYSCREEN) - 200; - SetWindowPos(window, HWND_TOP, posX, posY, 0, 0, SWP_NOSIZE); + SetWindowPos(nw, HWND_TOP, posX, posY, 0, 0, SWP_NOSIZE); // Hide the window ShowWindow(nw, SW_SHOW); @@ -402,4 +407,9 @@ int32_t show_notification(wchar_t * message, wchar_t * icon_path) { } return 1; +} + +void close_notification() { + SendMessage(nw, WM_CLOSE, 0, 0); + nw = NULL; } \ No newline at end of file diff --git a/native/libwinbridge/bridge.h b/native/libwinbridge/bridge.h index aca8d87..d7bfd6e 100644 --- a/native/libwinbridge/bridge.h +++ b/native/libwinbridge/bridge.h @@ -56,6 +56,15 @@ extern "C" int32_t get_active_window_executable(wchar_t * buffer, int32_t size); // UI +/* + * Show a window containing the notification. + */ extern "C" int32_t show_notification(wchar_t * message, wchar_t * icon_path); +/* + * Close the notification if present + */ +extern "C" void close_notification(); + + #endif //ESPANSO_BRIDGE_H \ No newline at end of file diff --git a/src/bridge/windows.rs b/src/bridge/windows.rs index 653e8cd..50f114b 100644 --- a/src/bridge/windows.rs +++ b/src/bridge/windows.rs @@ -9,6 +9,7 @@ extern { // UI pub fn show_notification(message: *const u16, icon_path: *const u16) -> i32; + pub fn close_notification(); // KEYBOARD pub fn register_keypress_callback(s: *const c_void, diff --git a/src/main.rs b/src/main.rs index 72a3fb2..21e31a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use crate::matcher::scrolling::ScrollingMatcher; use crate::engine::Engine; use crate::config::{ConfigSet, RuntimeConfigManager}; use crate::ui::UIManager; -use std::thread; +use std::{thread, time}; use clap::{App, Arg}; use std::path::Path; use std::sync::mpsc::Receiver; @@ -96,6 +96,8 @@ fn espanso_background(rxc: Receiver, config_set: ConfigSet) { let ui_manager = ui::get_uimanager(); ui_manager.notify("Hello guys"); + thread::sleep(time::Duration::from_millis(600)); + ui_manager.notify("There"); let clipboard_manager = clipboard::get_manager(); diff --git a/src/ui/windows.rs b/src/ui/windows.rs index 524c6dc..c03a188 100644 --- a/src/ui/windows.rs +++ b/src/ui/windows.rs @@ -1,23 +1,56 @@ use std::process::Command; -use crate::bridge::windows::show_notification; +use crate::bridge::windows::{show_notification, close_notification}; use widestring::U16CString; -use std::fs; -use log::{info}; +use std::{fs, thread, time}; +use log::{info, debug}; +use std::sync::Mutex; +use std::sync::Arc; const ICON_BINARY : &'static [u8] = include_bytes!("../res/win/espanso.bmp"); pub struct WindowsUIManager { icon_file: String, + id: Arc> } impl super::UIManager for WindowsUIManager { fn notify(&self, message: &str) { - unsafe { - let message = U16CString::from_str(message).unwrap(); - let icon_file = U16CString::from_str(&self.icon_file).unwrap(); - let res = show_notification(message.as_ptr(), icon_file.as_ptr()); - info!("{}", res); - } + let current_id: i32 = { + let mut id = self.id.lock().unwrap(); + *id += 1; + *id + }; + + // Setup a timeout to close the notification + let id = Arc::clone(&self.id); + thread::spawn(move || { + for i in 1..10 { + let duration = time::Duration::from_millis(200); + thread::sleep(duration); + + let new_id = id.lock().unwrap(); + if *new_id != current_id { + debug!("Cancelling notification close event with id {}", current_id); + return; + } + } + + unsafe { + close_notification(); + } + }); + + // Create and show a window notification + let message = message.to_owned(); + let icon_file = self.icon_file.clone(); + thread::spawn( move || { + unsafe { + let message = U16CString::from_str(message).unwrap(); + let icon_file = U16CString::from_str(&icon_file).unwrap(); + show_notification(message.as_ptr(), icon_file.as_ptr()); + } + }); + } } @@ -36,8 +69,10 @@ impl WindowsUIManager { info!("Extracted cached icon to: {}", icon_file); + let id = Arc::new(Mutex::new(0)); WindowsUIManager { - icon_file + icon_file, + id } } } \ No newline at end of file