#![allow(clippy::pedantic)]
#[allow(unused_imports)]
use std::os::raw::c_void;
use bevy::prelude::*;
use bevy::window::PrimaryWindow;
use bevy::winit::WinitWindows;
use bevy::{app::Plugin, prelude::Resource};
#[cfg(target_os = "windows")]
use winit::raw_window_handle;
#[cfg(target_os = "windows")]
use winit::raw_window_handle::HasWindowHandle;
#[cfg(target_os = "windows")]
use w::prelude::{Handle, shell_ITaskbarList3};
#[cfg(target_os = "windows")]
use w::{HWND, ITaskbarList4};
#[cfg(target_os = "windows")]
use winsafe::{self as w, co};
#[derive(Default)]
pub struct WindowUtilsPlugin {
pub icon: Option<bevy::asset::Handle<Image>>,
}
impl Plugin for WindowUtilsPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(WindowUtils {
window_icon: self.icon.clone(),
..Default::default()
})
.add_systems(Update, window_utils_resource_updated)
.add_systems(Update, update_is_maximized);
}
}
#[cfg(feature = "taskbar")]
pub struct TaskbarProgress {
pub progress: u64,
pub max: u64,
pub state: TaskbarState,
pub auto_no_progress: bool,
}
impl Default for TaskbarProgress {
fn default() -> Self {
TaskbarProgress {
progress: 0,
max: 100,
state: TaskbarState::Normal,
auto_no_progress: true,
}
}
}
#[derive(Copy, Clone)]
pub enum TaskbarState {
NoProgress = 0x0,
Indeterminate = 0x1,
Normal = 0x2,
Error = 0x4,
Paused = 0x8,
}
#[derive(Resource, Default)]
pub struct WindowUtils {
#[cfg(feature = "taskbar")]
pub taskbar_progress: Option<TaskbarProgress>,
pub window_icon: Option<bevy::asset::Handle<Image>>,
pub is_maximized: Option<bool>,
}
fn update_is_maximized(
mut window_utils: ResMut<WindowUtils>,
windows: NonSend<WinitWindows>,
window: Query<EntityRef, With<PrimaryWindow>>,
) {
for entity in window.iter() {
match windows.get_window(entity.id()) {
Some(window_wrapper) => {
window_utils.is_maximized = Some(window_wrapper.is_maximized());
}
None => {
warn_once!("winit is_maximized() interception failed, couldn't get the window.");
window_utils.is_maximized = None;
}
}
}
}
fn window_utils_resource_updated(
window_utils: Res<WindowUtils>,
windows: NonSend<WinitWindows>,
assets: Res<Assets<Image>>,
) {
if assets.is_changed() || window_utils.is_changed() {
let icon = window_utils
.window_icon
.as_ref()
.and_then(|i| assets.get(i))
.and_then(|i| {
::winit::window::Icon::from_rgba(
i.data.clone().unwrap(),
i.texture_descriptor.size.width,
i.texture_descriptor.size.height,
)
.ok()
});
for window in windows.windows.iter() {
window.1.set_window_icon(icon.clone())
}
#[cfg(all(feature = "taskbar", target_os = "windows"))]
if window_utils.is_changed() {
{
if let Some(progress) = &window_utils.taskbar_progress {
for window in windows.windows.iter() {
let itbl: ITaskbarList4 = w::CoCreateInstance(
&co::CLSID::TaskbarList,
None,
co::CLSCTX::INPROC_SERVER,
)
.unwrap();
unsafe {
match window.1.window_handle() {
Ok(handle) => {
if let raw_window_handle::RawWindowHandle::Win32(win_handle) =
handle.as_raw()
{
let hwnd = HWND::from_ptr(
isize::from(win_handle.hwnd) as *mut c_void
);
itbl.SetProgressValue(
&hwnd,
progress.progress,
progress.max,
)
.unwrap();
if progress.auto_no_progress
&& progress.progress >= progress.max
{
itbl.SetProgressState(&hwnd, co::TBPF::NOPROGRESS)
.unwrap();
} else {
itbl.SetProgressState(
&hwnd,
co::TBPF::from_raw(progress.state as u32),
)
.unwrap();
}
}
}
Err(e) => {
warn!("Couldn't set taskbar progress: {}", e);
}
}
}
}
}
}
}
}
}