From 9567a94dead6429deed8f4379c0dd5527ddac114 Mon Sep 17 00:00:00 2001 From: Psychotoxical Date: Thu, 9 Apr 2026 23:14:21 +0200 Subject: [PATCH] feat(windows): replace GDI taskbar icons with embedded .ico assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strips the legacy monochrome GDI drawing code and loads high-quality icons from embedded .ico files via CreateIconFromResourceEx. Fixes windows 0.58 import paths (Controls→Shell subclassing, TaskbarList CLSID) and adds proper cleanup for all four HICONs on WM_NCDESTROY. Co-Authored-By: Claude Sonnet 4.6 --- src-tauri/icons/windows/next.ico | Bin 0 -> 9662 bytes src-tauri/icons/windows/pause.ico | Bin 0 -> 9662 bytes src-tauri/icons/windows/play.ico | Bin 0 -> 9662 bytes src-tauri/icons/windows/prev.ico | Bin 0 -> 7358 bytes src-tauri/src/taskbar_win.rs | 215 ++++++++++-------------------- 5 files changed, 73 insertions(+), 142 deletions(-) create mode 100644 src-tauri/icons/windows/next.ico create mode 100644 src-tauri/icons/windows/pause.ico create mode 100644 src-tauri/icons/windows/play.ico create mode 100644 src-tauri/icons/windows/prev.ico diff --git a/src-tauri/icons/windows/next.ico b/src-tauri/icons/windows/next.ico new file mode 100644 index 0000000000000000000000000000000000000000..a9deaaf5b289c5b7c1aae8051b7b8e1d8a322c48 GIT binary patch literal 9662 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 Xto{9lyN>-kj_%(9{r~u{Vc`D&9ZD5% literal 0 HcmV?d00001 diff --git a/src-tauri/icons/windows/pause.ico b/src-tauri/icons/windows/pause.ico new file mode 100644 index 0000000000000000000000000000000000000000..54e7d19a35a3c543682f261e6587588d2cdeaac7 GIT binary patch literal 9662 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 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/windows/prev.ico b/src-tauri/icons/windows/prev.ico new file mode 100644 index 0000000000000000000000000000000000000000..a1b01a2e5b80133db16816643c0d20d2bbaebdfb GIT binary patch literal 7358 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

gs@?i>rk0jb^Uz zaOgS+iS}Qi)@YnV2uBt5RKk6@nLT~DdRkmRUM?O(pSjJ$$<4+@sWHCQ7Ux#Gc8u`yX{+$=Rl9`nQ7Tge&f%uOCOzSd~+q>O17 z`5F3j(8j7yI^?@GlgJ~i;9+>dKk$rbGdL~f)TNO)WR1GI4&^4!bwVq^(4y^h7@kdz zBVQjD*N$e32h+q;zqwT(e}u1Gwo)@VoGa+4NjF_~yAEFI0RAI=Co6Ax?n zN?~lf)Nz~4V-mkwQTGO$`ivxndKY=nqVajbQ?rO9fm=Jjwx(Bj2;A$Z0(y+H65vhdMElPyeWK~BkDwqmynl|g7 z4a^+i$UH?n)se@g#55f(=}Q%i4XQu&Nr*hnwZjP>#a_+If6!B!j>Hnv@rTBtlt##= zz$2Czs?4EJgGwxaF>yk-ggy~;0INCuAc?(_EE5F-{8#u9i2Y@(mjcnWneDmwg% zR&;M94hI%85_yRXZ^KTcoAia=4X*AFZe>G#bOTQ4LiDxrHI_1Uy3L&~Bai@tUWC{0 zk7y_RnaKfWYJiy@)J+fQMiS703@^f<1qn!$GGrM%cp`NqaN30Qp^r7aO)vtFu*+Dr z8M9&3=}nrOJ2DRGI2!kbn)Nl7GIx5+U7|!TY^?Ok6i$}3a~UDB!6E=7WDV&{S~WBt5DXp^ zx|SLGG5f~rYFes1{wM>tm_NudxN^(0D`%dY4V6F{gbS% N{}1;6|NXyF;2)@A*)0G7 literal 0 HcmV?d00001 diff --git a/src-tauri/src/taskbar_win.rs b/src-tauri/src/taskbar_win.rs index 1297b030..b4d847ac 100644 --- a/src-tauri/src/taskbar_win.rs +++ b/src-tauri/src/taskbar_win.rs @@ -8,41 +8,31 @@ use std::sync::atomic::{AtomicIsize, Ordering}; use tauri::{AppHandle, Emitter}; use windows::{ - core::Interface, Win32::{ Foundation::{HWND, LPARAM, LRESULT, WPARAM}, - Graphics::Gdi::{ - CreateCompatibleDC, CreateSolidBrush, DeleteDC, DeleteObject, GetStockObject, - Polygon, Rectangle as GdiRectangle, SelectObject, SetPolyFillMode, - WHITE_BRUSH, WINDING, HGDIOBJ, - }, System::Com::{ CoCreateInstance, CoInitializeEx, CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED, }, UI::{ - Controls::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass}, Shell::{ - CLSID_TaskbarList, ITaskbarList3, - THUMBBUTTON, THUMBBUTTONFLAGS, THUMBBUTTONMASK, THBN_CLICKED, + DefSubclassProc, ITaskbarList3, RemoveWindowSubclass, SetWindowSubclass, + TaskbarList, THUMBBUTTON, THUMBBUTTONFLAGS, THUMBBUTTONMASK, THBN_CLICKED, THB_FLAGS, THB_ICON, THB_TOOLTIP, }, WindowsAndMessaging::{ - CreateIconIndirect, DestroyIcon, ICONINFO, + CreateIconFromResourceEx, DestroyIcon, HICON, LR_DEFAULTCOLOR, WM_COMMAND, WM_NCDESTROY, }, }, }, }; -use windows::Win32::Graphics::Gdi::{ - CreateBitmap, RGBQUAD, -}; -use windows::Win32::UI::WindowsAndMessaging::HICON; -// ── Icon indices (positions in the per-button assignment) ──────────────────── -const IDX_PREV: u32 = 0; -const IDX_PLAY: u32 = 1; -const IDX_PAUSE: u32 = 2; -const IDX_NEXT: u32 = 3; +// ── Embedded ICO assets ────────────────────────────────────────────────────── + +static PREV_ICO: &[u8] = include_bytes!("../icons/windows/prev.ico"); +static PLAY_ICO: &[u8] = include_bytes!("../icons/windows/play.ico"); +static PAUSE_ICO: &[u8] = include_bytes!("../icons/windows/pause.ico"); +static NEXT_ICO: &[u8] = include_bytes!("../icons/windows/next.ico"); // Button IDs — arbitrary u32 values, must fit in WPARAM low-word. const BTN_PREV: u32 = 0xE001; @@ -56,125 +46,64 @@ const SUBCLASS_ID: usize = 0xC0DE_7A8B; // COM object and icons without managed state. static TASKBAR_PTR: AtomicIsize = AtomicIsize::new(0); static HWND_VAL: AtomicIsize = AtomicIsize::new(0); -// HICONs for play and pause, stored so update_taskbar_icon can swap them. + +// All four HICONs stored for WM_NCDESTROY cleanup and play/pause swapping. +static HICON_PREV: AtomicIsize = AtomicIsize::new(0); static HICON_PLAY: AtomicIsize = AtomicIsize::new(0); static HICON_PAUSE: AtomicIsize = AtomicIsize::new(0); +static HICON_NEXT: AtomicIsize = AtomicIsize::new(0); -// ── GDI icon generation ────────────────────────────────────────────────────── +// ── ICO resource loader ────────────────────────────────────────────────────── -// Icon size used for all thumbnail toolbar buttons. -const ICON_SIZE: i32 = 16; +/// Load the best-match image from a raw `.ico` file in memory and return an HICON. +/// +/// Parses the ICO directory to pick the entry with the highest bit depth +/// (32 bpp = true-colour + alpha), then passes the image bits directly to +/// `CreateIconFromResourceEx`. +/// +/// Note: `LookupIconIdFromDirectoryEx` operates on Win32 *resource* group-icon +/// format (GRPICONDIR), not raw `.ico` files, so we parse the ICO header ourselves. +unsafe fn load_icon_from_memory(bytes: &[u8]) -> HICON { + // ICO file layout: + // ICONDIR : reserved(2) + type(2) + count(2) + // ICONDIRENTRY[] : width(1) height(1) color_count(1) reserved(1) + // planes(2) bit_count(2) bytes_in_res(4) image_offset(4) + if bytes.len() < 6 { + return HICON::default(); + } + let count = u16::from_le_bytes([bytes[4], bytes[5]]) as usize; + if count == 0 || bytes.len() < 6 + count * 16 { + return HICON::default(); + } -/// Draw `f` onto a monochrome bitmap, then wrap it in an HICON with a -/// transparent background. The closure receives an HDC sized ICON_SIZE×ICON_SIZE. -unsafe fn make_icon(draw: F) -> HICON { - // XOR mask: white pixels become the icon colour against dark taskbar. - // AND mask: all zeros → every pixel is drawn (no transparency punch-out needed - // for the shape itself; the taskbar composites onto its own bg). - let xor_bits = vec![0xFFFFFFFFu32; (ICON_SIZE * ICON_SIZE) as usize]; // white canvas - let and_bits = vec![0u32; (ICON_SIZE * ICON_SIZE) as usize]; // fully opaque mask + // Pick the entry with the highest bit depth; 32 bpp carries alpha. + let mut best_idx = 0usize; + let mut best_bpp = 0u16; + for i in 0..count { + let base = 6 + i * 16; + let bpp = u16::from_le_bytes([bytes[base + 6], bytes[base + 7]]); + if bpp >= best_bpp { + best_bpp = bpp; + best_idx = i; + } + } - // Create a 32-bpp colour bitmap we can GDI-draw into, then copy to xor_bits. - // Simpler: build a 1bpp monochrome bitmap directly from bit arrays. - // We use a colour DC approach: draw white shapes on black, extract as the XOR mask. - let hdc_screen = CreateCompatibleDC(None); - // 1-bpp monochrome DIB: width=ICON_SIZE, height=ICON_SIZE, planes=1, bpp=1 - // For simplicity use CreateBitmap with 1bpp. - let hbm_xor = CreateBitmap(ICON_SIZE, ICON_SIZE, 1, 32, Some(xor_bits.as_ptr() as *const _)); - let hbm_and = CreateBitmap(ICON_SIZE, ICON_SIZE, 1, 1, Some(and_bits.as_ptr() as *const _)); + let entry = &bytes[6 + best_idx * 16..]; + let img_size = u32::from_le_bytes(entry[8..12].try_into().unwrap_or([0; 4])); + let img_offset = u32::from_le_bytes(entry[12..16].try_into().unwrap_or([0; 4])) as usize; - // Select xor bitmap into DC and draw. - let old = SelectObject(hdc_screen, hbm_xor); - // Fill black background. - let black_brush = CreateSolidBrush(windows::Win32::Foundation::COLORREF(0x00000000)); - GdiRectangle(hdc_screen, 0, 0, ICON_SIZE, ICON_SIZE); - DeleteObject(black_brush); + if img_size == 0 || img_offset + img_size as usize > bytes.len() { + return HICON::default(); + } - // Let caller draw white shapes. - draw(hdc_screen); - - SelectObject(hdc_screen, old); - DeleteDC(hdc_screen); - - let info = ICONINFO { - fIcon: true.into(), - xHotspot: 0, - yHotspot: 0, - hbmMask: hbm_and, - hbmColor: hbm_xor, - }; - let hicon = CreateIconIndirect(&info).unwrap_or_default(); - // Bitmaps are now owned by the HICON; do NOT DeleteObject them. - hicon -} - -/// ▶ single filled triangle pointing right. -unsafe fn icon_play() -> HICON { - make_icon(|hdc| { - let brush = CreateSolidBrush(windows::Win32::Foundation::COLORREF(0x00FFFFFF)); - let old = SelectObject(hdc, brush); - SetPolyFillMode(hdc, WINDING); - let pts = [ - windows::Win32::Foundation::POINT { x: 3, y: 1 }, - windows::Win32::Foundation::POINT { x: 3, y: ICON_SIZE - 2 }, - windows::Win32::Foundation::POINT { x: ICON_SIZE - 2, y: ICON_SIZE / 2 }, - ]; - Polygon(hdc, &pts); - SelectObject(hdc, old); - DeleteObject(brush); - }) -} - -/// ⏸ two filled rectangles. -unsafe fn icon_pause() -> HICON { - make_icon(|hdc| { - let brush = CreateSolidBrush(windows::Win32::Foundation::COLORREF(0x00FFFFFF)); - let old = SelectObject(hdc, brush); - GdiRectangle(hdc, 2, 1, 6, ICON_SIZE - 1); - GdiRectangle(hdc, 8, 1, 12, ICON_SIZE - 1); - SelectObject(hdc, old); - DeleteObject(brush); - }) -} - -/// ⏮ vertical bar + filled triangle pointing left. -unsafe fn icon_prev() -> HICON { - make_icon(|hdc| { - let brush = CreateSolidBrush(windows::Win32::Foundation::COLORREF(0x00FFFFFF)); - let old = SelectObject(hdc, brush); - SetPolyFillMode(hdc, WINDING); - // Bar - GdiRectangle(hdc, 1, 1, 4, ICON_SIZE - 1); - // Triangle pointing left - let pts = [ - windows::Win32::Foundation::POINT { x: ICON_SIZE - 2, y: 1 }, - windows::Win32::Foundation::POINT { x: ICON_SIZE - 2, y: ICON_SIZE - 2 }, - windows::Win32::Foundation::POINT { x: 5, y: ICON_SIZE / 2 }, - ]; - Polygon(hdc, &pts); - SelectObject(hdc, old); - DeleteObject(brush); - }) -} - -/// ⏭ filled triangle pointing right + vertical bar. -unsafe fn icon_next() -> HICON { - make_icon(|hdc| { - let brush = CreateSolidBrush(windows::Win32::Foundation::COLORREF(0x00FFFFFF)); - let old = SelectObject(hdc, brush); - SetPolyFillMode(hdc, WINDING); - // Triangle pointing right - let pts = [ - windows::Win32::Foundation::POINT { x: 1, y: 1 }, - windows::Win32::Foundation::POINT { x: 1, y: ICON_SIZE - 2 }, - windows::Win32::Foundation::POINT { x: ICON_SIZE - 5, y: ICON_SIZE / 2 }, - ]; - Polygon(hdc, &pts); - // Bar - GdiRectangle(hdc, ICON_SIZE - 4, 1, ICON_SIZE - 1, ICON_SIZE - 1); - SelectObject(hdc, old); - DeleteObject(brush); - }) + CreateIconFromResourceEx( + &bytes[img_offset..img_offset + img_size as usize], + true, // fIcon = TRUE + 0x0003_0000, // dwVer = 3.0 (required by the API) + 0, 0, // cxDesired / cyDesired — 0 lets the system choose + LR_DEFAULTCOLOR, + ) + .unwrap_or_default() } // ── Button descriptors ─────────────────────────────────────────────────────── @@ -252,11 +181,11 @@ unsafe extern "system" fn subclass_proc( drop(Box::from_raw(raw as *mut ITaskbarList3)); } HWND_VAL.store(0, Ordering::SeqCst); - // Destroy stored HICONs. - let hp = HICON_PLAY.swap(0, Ordering::SeqCst); - if hp != 0 { let _ = DestroyIcon(HICON(hp as *mut _)); } - let hpa = HICON_PAUSE.swap(0, Ordering::SeqCst); - if hpa != 0 { let _ = DestroyIcon(HICON(hpa as *mut _)); } + // Destroy all stored HICONs. + for cell in [&HICON_PREV, &HICON_PLAY, &HICON_PAUSE, &HICON_NEXT] { + let h = cell.swap(0, Ordering::SeqCst); + if h != 0 { let _ = DestroyIcon(HICON(h as *mut _)); } + } } DefSubclassProc(hwnd, msg, wparam, lparam) @@ -271,7 +200,7 @@ pub fn init(app: &AppHandle, hwnd_raw: isize) { let hwnd = HWND(hwnd_raw as *mut _); let taskbar: ITaskbarList3 = match CoCreateInstance( - &CLSID_TaskbarList, None, CLSCTX_INPROC_SERVER, + &TaskbarList, None, CLSCTX_INPROC_SERVER, ) { Ok(t) => t, Err(e) => { eprintln!("[psysonic] taskbar: CoCreateInstance failed: {e}"); return; } @@ -282,14 +211,16 @@ pub fn init(app: &AppHandle, hwnd_raw: isize) { return; } - let h_prev = icon_prev(); - let h_play = icon_play(); - let h_pause = icon_pause(); - let h_next = icon_next(); + let h_prev = load_icon_from_memory(PREV_ICO); + let h_play = load_icon_from_memory(PLAY_ICO); + let h_pause = load_icon_from_memory(PAUSE_ICO); + let h_next = load_icon_from_memory(NEXT_ICO); - // Store play/pause HICONs for later swapping. + // Store all HICONs for cleanup and play/pause swapping. + HICON_PREV .store(h_prev .0 as isize, Ordering::SeqCst); HICON_PLAY .store(h_play .0 as isize, Ordering::SeqCst); HICON_PAUSE.store(h_pause.0 as isize, Ordering::SeqCst); + HICON_NEXT .store(h_next .0 as isize, Ordering::SeqCst); let mut buttons = make_buttons(h_prev, h_play, h_next); if let Err(e) = taskbar.ThumbBarAddButtons(hwnd, &mut buttons) { @@ -302,7 +233,7 @@ pub fn init(app: &AppHandle, hwnd_raw: isize) { HWND_VAL .store(hwnd_raw, Ordering::SeqCst); let data = Box::into_raw(Box::new(SubclassData { app: app.clone() })); - if SetWindowSubclass(hwnd, Some(subclass_proc), SUBCLASS_ID, data as usize).is_err() { + if !SetWindowSubclass(hwnd, Some(subclass_proc), SUBCLASS_ID, data as usize).as_bool() { eprintln!("[psysonic] taskbar: SetWindowSubclass failed"); drop(Box::from_raw(data)); }