diff --git a/native/libwinbridge/bridge.cpp b/native/libwinbridge/bridge.cpp index a9302ac..154e927 100644 --- a/native/libwinbridge/bridge.cpp +++ b/native/libwinbridge/bridge.cpp @@ -7,6 +7,8 @@ #define UNICODE #include +#include +#include // How many milliseconds must pass between keystrokes to refresh the keyboard layout const long refreshKeyboardLayoutInterval = 2000; @@ -275,11 +277,21 @@ int32_t get_active_window_executable(wchar_t * buffer, int32_t size) { } // UI +#define APPWM_ICONNOTIFY (WM_APP + 1) const wchar_t* const notification_winclass = L"EspansoNotification"; HWND nw = NULL; HWND hwnd_st_u = NULL; -HBITMAP g_espanso_icon = NULL; +HBITMAP g_espanso_bmp = NULL; +HICON g_espanso_ico = NULL; + +MenuItemCallback menu_item_callback = NULL; +void * ui_manager_instance; + +void register_menu_item_callback(void *self, MenuItemCallback callback) { + menu_item_callback = callback; + ui_manager_instance = self; +} /* * Message handler procedure for the notification window @@ -293,8 +305,49 @@ LRESULT CALLBACK notification_worker_procedure(HWND window, unsigned int msg, WP case WM_DESTROY: std::cout << "\ndestroying window\n"; PostQuitMessage(0); - DeleteObject(g_espanso_icon); + DeleteObject(g_espanso_bmp); return 0L; + case WM_MENUSELECT: + { + HMENU hmenu = (HMENU)lp; + UINT idItem = (UINT)LOWORD(wp); + UINT flags = (UINT)HIWORD(wp); + + if (flags & MF_CHECKED) { + // TODO: callback + } + + break; + } + case APPWM_ICONNOTIFY: + { + switch (lp) + { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + HMENU hPopupMenu = CreatePopupMenu(); + + // Create the menu + + int32_t count; + MenuItem items[100]; + + // Load the items + menu_item_callback(ui_manager_instance, items, &count); + + for (int i = 0; i i32; // UI - pub fn initialize_ui(icon_path: *const u16) -> i32; + pub fn initialize_ui(ico_path: *const u16, bmp_path: *const u16) -> i32; pub fn show_notification(message: *const u16) -> i32; pub fn close_notification(); + pub fn register_menu_item_callback(s: *const c_void, + cb: extern fn(_self: *mut c_void, *mut WindowsMenuItem, + *mut i32)); // KEYBOARD pub fn register_keypress_callback(s: *const c_void, diff --git a/src/res/win/espanso.ico b/src/res/win/espanso.ico new file mode 100644 index 0000000..1d1c06c Binary files /dev/null and b/src/res/win/espanso.ico differ diff --git a/src/ui/windows.rs b/src/ui/windows.rs index 563ed7b..1f56edf 100644 --- a/src/ui/windows.rs +++ b/src/ui/windows.rs @@ -1,15 +1,17 @@ use std::process::Command; -use crate::bridge::windows::{show_notification, close_notification, initialize_ui}; +use crate::bridge::windows::{show_notification, close_notification, initialize_ui, WindowsMenuItem, register_menu_item_callback}; use widestring::U16CString; use std::{fs, thread, time}; use log::{info, debug}; use std::sync::Mutex; use std::sync::Arc; +use std::fs::create_dir_all; +use std::os::raw::c_void; -const ICON_BINARY : &'static [u8] = include_bytes!("../res/win/espanso.bmp"); +const BMP_BINARY : &'static [u8] = include_bytes!("../res/win/espanso.bmp"); +const ICO_BINARY : &'static [u8] = include_bytes!("../res/win/espanso.ico"); pub struct WindowsUIManager { - icon_file: String, id: Arc> } @@ -51,31 +53,79 @@ impl super::UIManager for WindowsUIManager { impl WindowsUIManager { pub fn new() -> WindowsUIManager { - let res = dirs::cache_dir(); - let mut icon_file:String = "".to_owned(); - if let Some(cache_dir) = res { - let espanso_icon_file = cache_dir.join("espansoicon.bmp"); + let data_dir = dirs::data_dir().expect("Can't obtain data_dir(), terminating."); - fs::write(&espanso_icon_file, ICON_BINARY) - .expect("Unable to write windows icon file"); + let espanso_dir = data_dir.join("espanso"); - icon_file = espanso_icon_file.to_str().unwrap_or(&"".to_owned()).to_owned(); + let res = create_dir_all(&espanso_dir); + + info!("Initializing Espanso resources in {}", espanso_dir.as_path().display()); + + let espanso_bmp_image = espanso_dir.join("espansoicon.bmp"); + if espanso_bmp_image.exists() { + info!("BMP already initialized, skipping."); + }else { + fs::write(&espanso_bmp_image, BMP_BINARY) + .expect("Unable to write windows bmp file"); + + info!("Extracted bmp icon to: {}", espanso_bmp_image.to_str().unwrap_or("error")); } - info!("Extracted cached icon to: {}", icon_file); + let espanso_ico_image = espanso_dir.join("espanso.ico"); + if espanso_ico_image.exists() { + info!("ICO already initialized, skipping."); + }else { + fs::write(&espanso_ico_image, ICO_BINARY) + .expect("Unable to write windows ico file"); + + info!("Extracted 'ico' icon to: {}", espanso_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 id = Arc::new(Mutex::new(0)); - let icon_file_c = U16CString::from_str(&icon_file).unwrap(); + let ico_file_c = U16CString::from_str(ico_icon).unwrap(); + let bmp_file_c = U16CString::from_str(bmp_icon).unwrap(); + + let manager = WindowsUIManager { + id + }; + + // Setup the menu item callback + unsafe { + let self_ptr = &manager as *const WindowsUIManager as *const c_void; + register_menu_item_callback(self_ptr, menu_item_callback); + } thread::spawn(move || { unsafe { - initialize_ui(icon_file_c.as_ptr()); + initialize_ui(ico_file_c.as_ptr(), bmp_file_c.as_ptr()); } }); - WindowsUIManager { - icon_file, - id - } + manager + } +} + +// NATIVE + +extern fn menu_item_callback(_self: *mut c_void, items: *mut WindowsMenuItem, count: *mut i32) { + unsafe { + let _self = _self as *mut WindowsUIManager; + + let str = U16CString::from_str("Test").unwrap_or_default(); + let mut str_buff : [u16; 100] = [0; 100]; + std::ptr::copy(str.as_ptr(), str_buff.as_mut_ptr(), str.len()); + let item = WindowsMenuItem { + item_id: 1, + item_type: 1, + item_name: str_buff, + }; + + let items = unsafe { std::slice::from_raw_parts_mut(items, 100) }; + + items[0] = item; + *count = 1; } } \ No newline at end of file