Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Taskbar progress #3241

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Unreleased` header.
- On macOS, add services menu.
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
- On Web, fix setting cursor icon overriding cursor visibility.
- On Windows, add `WindowExt::set_taskbar_progress`.

# 0.29.5

Expand Down
1 change: 1 addition & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ If your PR makes notable changes to Winit's features, please update this section
* Setting a menu bar
* `WS_EX_NOREDIRECTIONBITMAP` support
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
* Setting the taskbar progress

### macOS
* Window activation policy
Expand Down
5 changes: 5 additions & 0 deletions examples/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ fn main() -> Result<(), impl std::error::Error> {
} => {
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
window.set_cursor_icon(CURSORS[cursor_idx]);
#[cfg(windows_platform)]
{
use winit::platform::windows::WindowExtWindows;
window.set_taskbar_progress(cursor_idx as f32 / CURSORS.len() as f32);
}
if cursor_idx < CURSORS.len() - 1 {
cursor_idx += 1;
} else {
Expand Down
11 changes: 11 additions & 0 deletions src/platform/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ pub trait WindowExtWindows {
///
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn set_undecorated_shadow(&self, shadow: bool);

/// Sets the progress shown in the taskbar.
///
/// If the value is 0.0 the taskbar button will return to its normal state. Any value outside of
/// 0.0 and 1.0 will set the state to indeterminate.
fn set_taskbar_progress(&self, value: f32);
}

impl WindowExtWindows for Window {
Expand All @@ -158,6 +164,11 @@ impl WindowExtWindows for Window {
fn set_undecorated_shadow(&self, shadow: bool) {
self.window.set_undecorated_shadow(shadow)
}

#[inline]
fn set_taskbar_progress(&self, value: f32) {
self.window.set_taskbar_progress(value)
}
}

/// Additional methods on `WindowBuilder` that are specific to Windows.
Expand Down
77 changes: 75 additions & 2 deletions src/platform_impl/windows/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
use std::ffi::c_void;

use windows_sys::{
core::{IUnknown, GUID, HRESULT},
core::{IUnknown, GUID, HRESULT, PCWSTR},
Win32::{
Foundation::{BOOL, HWND, POINTL},
Foundation::{BOOL, HWND, POINTL, RECT},
System::Com::{
IAdviseSink, IDataObject, IEnumFORMATETC, IEnumSTATDATA, FORMATETC, STGMEDIUM,
},
UI::{Controls::HIMAGELIST, WindowsAndMessaging::HICON},
},
};

Expand Down Expand Up @@ -130,6 +131,71 @@ pub struct ITaskbarList2 {
pub lpVtbl: *const ITaskbarList2Vtbl,
}

#[repr(C)]
pub struct ITaskbarList3Vtbl {
pub parent: ITaskbarList2Vtbl,
pub SetProgressValue: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwnd: HWND,
ullCompleted: u64,
ullTotal: u64,
) -> HRESULT,
pub SetProgressState:
unsafe extern "system" fn(This: *mut ITaskbarList3, hwnd: HWND, tbpFlags: u32) -> HRESULT,
pub RegisterTab: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwndTab: HWND,
hwndMDI: HWND,
) -> HRESULT,
pub UnregisterTab:
unsafe extern "system" fn(This: *mut ITaskbarList3, hwndTab: HWND) -> HRESULT,
pub SetTabOrder: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwndTab: HWND,
hwndInsertBefore: HWND,
) -> HRESULT,
pub SetTabActive: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwndTab: HWND,
hwndMDI: HWND,
) -> HRESULT,
pub ThumbBarAddButtons: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwnd: HWND,
cButtons: u32,
pButton: *const c_void,
) -> HRESULT,
pub ThumbBarUpdateButtons: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwnd: HWND,
cButtons: u32,
pButton: *const c_void,
) -> HRESULT,
pub ThumbBarSetImageList: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwnd: HWND,
himl: HIMAGELIST,
) -> HRESULT,
pub SetOverlayIcon: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwnd: HWND,
hIcon: HICON,
pszDescription: PCWSTR,
) -> HRESULT,
pub SetThumbnailTooltip:
unsafe extern "system" fn(This: *mut ITaskbarList3, hwnd: HWND, pszTip: PCWSTR) -> HRESULT,
pub SetThumbnailClip: unsafe extern "system" fn(
This: *mut ITaskbarList3,
hwnd: HWND,
prcClip: *const RECT,
) -> HRESULT,
}

#[repr(C)]
pub struct ITaskbarList3 {
pub lpVtbl: *const ITaskbarList3Vtbl,
}

pub const CLSID_TaskbarList: GUID = GUID {
data1: 0x56fdf344,
data2: 0xfd6d,
Expand All @@ -150,3 +216,10 @@ pub const IID_ITaskbarList2: GUID = GUID {
data3: 0x429b,
data4: [0xa6, 0x6e, 0x19, 0x35, 0xe4, 0x4f, 0x43, 0x17],
};

pub const IID_ITaskbarList3: GUID = GUID {
data1: 0xea1afb91,
data2: 0x9e28,
data3: 0x4b86,
data4: [0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf],
};
65 changes: 61 additions & 4 deletions src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ use crate::{
platform_impl::platform::{
dark_mode::try_theme,
definitions::{
CLSID_TaskbarList, IID_ITaskbarList, IID_ITaskbarList2, ITaskbarList, ITaskbarList2,
CLSID_TaskbarList, IID_ITaskbarList, IID_ITaskbarList2, IID_ITaskbarList3,
ITaskbarList, ITaskbarList2, ITaskbarList3,
},
dpi::{dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_dpi},
drop_handler::FileDropHandler,
Expand Down Expand Up @@ -1059,6 +1060,13 @@ impl Window {
);
}
}

#[inline]
pub fn set_taskbar_progress(&self, progress: f32) {
let window = self.window.clone();

unsafe { set_taskbar_progress(window.0, progress) };
}
}

impl Drop for Window {
Expand Down Expand Up @@ -1423,6 +1431,7 @@ thread_local! {

static TASKBAR_LIST: Cell<*mut ITaskbarList> = Cell::new(ptr::null_mut());
static TASKBAR_LIST2: Cell<*mut ITaskbarList2> = Cell::new(ptr::null_mut());
static TASKBAR_LIST3: Cell<*mut ITaskbarList3> = Cell::new(ptr::null_mut());
}

pub fn com_initialized() {
Expand Down Expand Up @@ -1472,8 +1481,9 @@ unsafe fn taskbar_mark_fullscreen(handle: HWND, fullscreen: bool) {
})
}

pub(crate) unsafe fn set_skip_taskbar(hwnd: HWND, skip: bool) {
pub(crate) unsafe fn set_skip_taskbar(handle: HWND, skip: bool) {
com_initialized();

TASKBAR_LIST.with(|task_bar_list_ptr| {
let mut task_bar_list = task_bar_list_ptr.get();

Expand Down Expand Up @@ -1503,10 +1513,10 @@ pub(crate) unsafe fn set_skip_taskbar(hwnd: HWND, skip: bool) {
task_bar_list = task_bar_list_ptr.get();
if skip {
let delete_tab = unsafe { (*(*task_bar_list).lpVtbl).DeleteTab };
unsafe { delete_tab(task_bar_list, hwnd) };
unsafe { delete_tab(task_bar_list, handle) };
} else {
let add_tab = unsafe { (*(*task_bar_list).lpVtbl).AddTab };
unsafe { add_tab(task_bar_list, hwnd) };
unsafe { add_tab(task_bar_list, handle) };
}
});
}
Expand Down Expand Up @@ -1556,3 +1566,50 @@ unsafe fn force_window_active(handle: HWND) {

unsafe { SetForegroundWindow(handle) };
}

pub(crate) unsafe fn set_taskbar_progress(handle: HWND, value: f32) {
com_initialized();

TASKBAR_LIST3.with(|task_bar_list3_ptr| {
let mut task_bar_list3 = task_bar_list3_ptr.get();

if task_bar_list3.is_null() {
let hr = unsafe {
CoCreateInstance(
&CLSID_TaskbarList,
ptr::null_mut(),
CLSCTX_ALL,
&IID_ITaskbarList3,
&mut task_bar_list3 as *mut _ as *mut _,
)
};
if hr != S_OK {
// In visual studio retrieving the taskbar list fails
return;
}

let hr_init = unsafe { (*(*task_bar_list3).lpVtbl).parent.parent.HrInit };
if unsafe { hr_init(task_bar_list3.cast()) } != S_OK {
// In some old windows, the taskbar object could not be created, we just ignore it
return;
}
task_bar_list3_ptr.set(task_bar_list3)
}

task_bar_list3 = task_bar_list3_ptr.get();
if (0.0..=1.0).contains(&value) {
let set_progress_value = unsafe { (*(*task_bar_list3).lpVtbl).SetProgressValue };
unsafe {
set_progress_value(
task_bar_list3,
handle,
(value * u16::MAX as f32) as u64,
u16::MAX as u64,
)
};
} else {
let set_progress_state = unsafe { (*(*task_bar_list3).lpVtbl).SetProgressState };
unsafe { set_progress_state(task_bar_list3, handle, 0x00000001) };
}
})
}