From 9450329e3ab70aa3608ef44207df19cfdddf45a0 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 21 Jul 2022 14:42:05 +0200 Subject: [PATCH] fix(windows): subscribe to taskbar restart event, closes #476 (#487) * fix(windows): subscribe to taskbar restart event, closes #476 --- .changes/taskbar-restart.md | 6 +++ src/platform_impl/windows/event_loop.rs | 27 +++++++---- src/platform_impl/windows/system_tray.rs | 59 +++++++++++++++++------ src/platform_impl/windows/window.rs | 25 +++++----- src/platform_impl/windows/window_state.rs | 4 ++ 5 files changed, 86 insertions(+), 35 deletions(-) create mode 100644 .changes/taskbar-restart.md diff --git a/.changes/taskbar-restart.md b/.changes/taskbar-restart.md new file mode 100644 index 000000000..1ccf901a5 --- /dev/null +++ b/.changes/taskbar-restart.md @@ -0,0 +1,6 @@ +--- +"tao": "patch" +--- + +On Windows, subscribe to taskbar restart event and re-add the system tray icon. +Also skip the window from the taskbar if it was already skipped. diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 30f31ad9b..86a0c1a8f 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -56,6 +56,7 @@ use crate::{ minimal_ime::is_msg_ime_related, monitor::{self, MonitorHandle}, raw_input, util, + window::set_skip_taskbar, window_state::{CursorFlags, WindowFlags, WindowState}, wrap_device_id, WindowId, DEVICE_ID, }, @@ -540,16 +541,16 @@ impl EventLoopProxy { type WaitUntilInstantBox = Box; lazy_static! { - // Message sent by the `EventLoopProxy` when we want to wake up the thread. - // WPARAM and LPARAM are unused. + /// Message sent by the `EventLoopProxy` when we want to wake up the thread. + /// WPARAM and LPARAM are unused. static ref USER_EVENT_MSG_ID: u32 = { unsafe { RegisterWindowMessageA("Tao::WakeupMsg") } }; - // Message sent when we want to execute a closure in the thread. - // WPARAM contains a Box> that must be retrieved with `Box::from_raw`, - // and LPARAM is unused. + /// Message sent when we want to execute a closure in the thread. + /// WPARAM contains a Box> that must be retrieved with `Box::from_raw`, + /// and LPARAM is unused. static ref EXEC_MSG_ID: u32 = { unsafe { RegisterWindowMessageA("Tao::ExecMsg") @@ -578,18 +579,23 @@ lazy_static! { RegisterWindowMessageA("Tao::CancelWaitUntil") } }; - // Message sent by a `Window` when it wants to be destroyed by the main thread. - // WPARAM and LPARAM are unused. + /// Message sent by a `Window` when it wants to be destroyed by the main thread. + /// WPARAM and LPARAM are unused. pub static ref DESTROY_MSG_ID: u32 = { unsafe { RegisterWindowMessageA("Tao::DestroyMsg") } }; - // WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the - // documentation in the `window_state` module for more information. + /// WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the + /// documentation in the `window_state` module for more information. pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { RegisterWindowMessageA("Tao::SetRetainMaximized") }; + /// When the taskbar is created, it registers a message with the "TaskbarCreated" string and then broadcasts this message to all top-level windows + /// When the application receives this message, it should assume that any taskbar icons it added have been removed and add them again. + pub static ref S_U_TASKBAR_RESTART: u32 = unsafe { + RegisterWindowMessageA("TaskbarCreated") + }; static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec = unsafe { let class_name= util::encode_wide("Tao Thread Event Target"); @@ -2034,6 +2040,9 @@ unsafe fn public_window_callback_inner( f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam.0 != 0) }); result = ProcResult::Value(LRESULT(0)); + } else if msg == *S_U_TASKBAR_RESTART { + let window_state = subclass_input.window_state.lock(); + set_skip_taskbar(window, window_state.skip_taskbar); } } }; diff --git a/src/platform_impl/windows/system_tray.rs b/src/platform_impl/windows/system_tray.rs index c3e4658b1..a68a8bd23 100644 --- a/src/platform_impl/windows/system_tray.rs +++ b/src/platform_impl/windows/system_tray.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use super::{ + event_loop::S_U_TASKBAR_RESTART, menu::{subclass_proc as menu_subclass_proc, Menu, MenuHandler}, util, OsError, }; @@ -27,12 +28,15 @@ use windows::{ const WM_USER_TRAYICON: u32 = 6001; const WM_USER_UPDATE_TRAYMENU: u32 = 6002; -const TRAYICON_UID: u32 = 6003; -const TRAY_SUBCLASS_ID: usize = 6004; -const TRAY_MENU_SUBCLASS_ID: usize = 6005; +const WM_USER_UPDATE_TRAYICON: u32 = 6003; +const TRAYICON_UID: u32 = 6004; +const TRAY_SUBCLASS_ID: usize = 6005; +const TRAY_MENU_SUBCLASS_ID: usize = 6006; struct TrayLoopData { + hwnd: HWND, hmenu: Option, + icon: Icon, sender: Box)>, } @@ -81,34 +85,28 @@ impl SystemTrayBuilder { hinstance, std::ptr::null_mut(), ); - if !IsWindow(hwnd).as_bool() { return Err(os_error!(OsError::CreationError( "Unable to get valid mutable pointer for CreateWindowEx" ))); } - let mut nid = NOTIFYICONDATAW { - uFlags: NIF_MESSAGE, - hWnd: hwnd, - uID: TRAYICON_UID, - uCallbackMessage: WM_USER_TRAYICON, - ..std::mem::zeroed() - }; + let hicon = self.icon.inner.as_raw_handle(); - if !Shell_NotifyIconW(NIM_ADD, &mut nid as _).as_bool() { + if !register_tray_icon(hwnd, hicon) { return Err(os_error!(OsError::CreationError( "Error with shellapi::Shell_NotifyIconW" ))); } - let mut system_tray = SystemTray { hwnd }; - system_tray.set_icon(self.icon); + let system_tray = SystemTray { hwnd: hwnd.clone() }; // system_tray event handler let event_loop_runner = window_target.p.runner_shared.clone(); let traydata = TrayLoopData { + hwnd, hmenu, + icon: self.icon, sender: Box::new(move |event| { if let Ok(e) = event.map_nonuser_event() { event_loop_runner.send_event(e) @@ -162,6 +160,14 @@ impl SystemTray { if !Shell_NotifyIconW(NIM_MODIFY, &mut nid as _).as_bool() { debug!("Error setting icon"); } + + // send the new icon to the subclass proc to store it in the tray data + SendMessageW( + self.hwnd, + WM_USER_UPDATE_TRAYICON, + WPARAM(Box::into_raw(Box::new(icon)) as _), + LPARAM(0), + ); } } @@ -217,6 +223,18 @@ unsafe extern "system" fn tray_subclass_proc( subclass_input.hmenu = Some(HMENU(wparam.0 as _)); } + if msg == WM_USER_UPDATE_TRAYICON { + let icon = wparam.0 as *mut Icon; + subclass_input.icon = (*icon).clone(); + } + + if msg == *S_U_TASKBAR_RESTART { + register_tray_icon( + subclass_input.hwnd, + subclass_input.icon.inner.as_raw_handle(), + ); + } + if msg == WM_USER_TRAYICON && matches!( lparam.0 as u32, @@ -295,3 +313,16 @@ unsafe fn show_tray_menu(hwnd: HWND, menu: HMENU, x: i32, y: i32) { std::ptr::null_mut(), ); } + +unsafe fn register_tray_icon(hwnd: HWND, hicon: HICON) -> bool { + let mut nid = NOTIFYICONDATAW { + uFlags: NIF_MESSAGE | NIF_ICON, + hWnd: hwnd, + hIcon: hicon, + uID: TRAYICON_UID, + uCallbackMessage: WM_USER_TRAYICON, + ..std::mem::zeroed() + }; + + Shell_NotifyIconW(NIM_ADD, &mut nid as _).as_bool() +} diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index f943cc727..d49022a34 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -785,18 +785,8 @@ impl Window { #[inline] pub(crate) fn set_skip_taskbar(&self, skip: bool) { - unsafe { - com_initialized(); - let taskbar_list: ITaskbarList = - CoCreateInstance(&TaskbarList, None, CLSCTX_SERVER).expect("failed to create TaskBarList"); - if skip { - taskbar_list - .DeleteTab(self.hwnd()) - .expect("DeleteTab failed"); - } else { - taskbar_list.AddTab(self.hwnd()).expect("AddTab failed"); - } - } + self.window_state.lock().skip_taskbar = skip; + unsafe { set_skip_taskbar(self.hwnd(), skip) }; } } @@ -1154,6 +1144,17 @@ unsafe fn force_window_active(handle: HWND) { SetForegroundWindow(handle); } +pub(crate) unsafe fn set_skip_taskbar(hwnd: HWND, skip: bool) { + com_initialized(); + let taskbar_list: ITaskbarList = + CoCreateInstance(&TaskbarList, None, CLSCTX_SERVER).expect("failed to create TaskBarList"); + if skip { + taskbar_list.DeleteTab(hwnd).expect("DeleteTab failed"); + } else { + taskbar_list.AddTab(hwnd).expect("AddTab failed"); + } +} + pub fn hit_test(hwnd: *mut libc::c_void, cx: i32, cy: i32) -> LRESULT { let hwnd = HWND(hwnd as _); let mut window_rect = RECT::default(); diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index bb7fb8588..61e59ed42 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -30,6 +30,8 @@ pub struct WindowState { pub saved_window: Option, pub scale_factor: f64, + pub skip_taskbar: bool, + pub modifiers_state: ModifiersState, pub fullscreen: Option, pub current_theme: Theme, @@ -122,6 +124,8 @@ impl WindowState { saved_window: None, scale_factor, + skip_taskbar: false, + modifiers_state: ModifiersState::default(), fullscreen: None, current_theme,