From 768c286d4a122e24daf50e06c5cd3b4770f2510f Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Thu, 11 Jun 2026 10:19:25 -0400 Subject: [PATCH] Fix Windows badge API for windows-rs 0.61 + add libfuse2 for Linux AppImage - Move BOOL import to windows::core (removed from Win32::Foundation in 0.61) - Move CreateBitmap import from WindowsAndMessaging to Win32::Graphics::Gdi - Wrap nullable handles in Some() for GetDC, CreateCompatibleDC, CreateDIBSection, ReleaseDC (new Option API in 0.61) - Add .into() for all SelectObject/DeleteObject GDI handle args (now HGDIOBJ) - Use FONT_PITCH_AND_FAMILY type directly in CreateFontW instead of u32 cast - Make DrawTextW slice mutable (&mut [u16] required in 0.61) - Use hicon.unwrap_or_default() for SetOverlayIcon (takes HICON not Option) - Add libfuse2 to apt-get for Linux AppImage bundler (FUSE mount fallback) Co-Authored-By: Claude Sonnet 4.6 --- .gitea/workflows/release.yml | 3 +- src-tauri/src/lib.rs | 83 ++++++++++++++++++++---------------- 2 files changed, 49 insertions(+), 37 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index bfdb7b5..0f5409f 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -134,7 +134,8 @@ jobs: libayatana-appindicator3-dev \ librsvg2-dev \ patchelf \ - xdg-utils + xdg-utils \ + libfuse2 - uses: dtolnay/rust-toolchain@stable diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 952ea9a..390f5a9 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -97,21 +97,20 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> { #[cfg(target_os = "windows")] { use windows::{ - core::PCWSTR, + core::{BOOL, PCWSTR}, Win32::{ - Foundation::{BOOL, COLORREF, HWND, RECT}, + Foundation::{COLORREF, HWND, RECT}, Graphics::Gdi::{ - CreateCompatibleDC, CreateDIBSection, CreatePen, CreateSolidBrush, - DeleteDC, DeleteObject, DIB_RGB_COLORS, DrawTextW, Ellipse, + CreateBitmap, CreateCompatibleDC, CreateDIBSection, CreateFontW, CreatePen, + CreateSolidBrush, DeleteDC, DeleteObject, DIB_RGB_COLORS, DrawTextW, Ellipse, ReleaseDC, SelectObject, SetBkMode, SetTextColor, BITMAPINFO, - BITMAPINFOHEADER, BI_RGB, DT_CENTER, DT_SINGLELINE, DT_VCENTER, - PS_NULL, TRANSPARENT, CreateFontW, DEFAULT_CHARSET, DEFAULT_PITCH, - DEFAULT_QUALITY, CLIP_DEFAULT_PRECIS, FW_BOLD, FF_DONTCARE, - OUT_DEFAULT_PRECIS, + BITMAPINFOHEADER, BI_RGB, CLIP_DEFAULT_PRECIS, DEFAULT_CHARSET, DEFAULT_PITCH, + DEFAULT_QUALITY, DT_CENTER, DT_SINGLELINE, DT_VCENTER, FF_DONTCARE, + FONT_PITCH_AND_FAMILY, FW_BOLD, OUT_DEFAULT_PRECIS, PS_NULL, TRANSPARENT, }, UI::{ Shell::{ITaskbarList3, TaskbarList}, - WindowsAndMessaging::{CreateBitmap, CreateIconIndirect, DestroyIcon, ICONINFO}, + WindowsAndMessaging::{CreateIconIndirect, DestroyIcon, HICON, ICONINFO}, }, System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER}, }, @@ -119,18 +118,18 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> { let hwnd = HWND(window.hwnd().map_err(|e| e.to_string())?.0 as _); - let hicon = if count > 0 { + let hicon: Option = if count > 0 { let label = if count > 99 { "99+".to_string() } else { count.to_string() }; - let label_wide: Vec = label.encode_utf16().chain(std::iter::once(0)).collect(); + let mut label_wide: Vec = label.encode_utf16().chain(std::iter::once(0)).collect(); unsafe { let size = 16i32; - let hdc_screen = windows::Win32::Graphics::Gdi::GetDC(HWND(std::ptr::null_mut())); - let hdc = CreateCompatibleDC(hdc_screen); + let hdc_screen = windows::Win32::Graphics::Gdi::GetDC(None); + let hdc = CreateCompatibleDC(Some(hdc_screen)); let bmi = BITMAPINFO { bmiHeader: BITMAPINFOHEADER { @@ -145,32 +144,41 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> { ..Default::default() }; let mut bits: *mut std::ffi::c_void = std::ptr::null_mut(); - let hbm_color = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &mut bits, None, 0) - .map_err(|e| e.to_string())?; - let old_bm = SelectObject(hdc, hbm_color); + let hbm_color = + CreateDIBSection(Some(hdc), &bmi, DIB_RGB_COLORS, &mut bits, None, 0) + .map_err(|e| e.to_string())?; + let old_bm = SelectObject(hdc, hbm_color.into()); let hbrush = CreateSolidBrush(COLORREF(0x003030DD)); - let old_brush = SelectObject(hdc, hbrush); + let old_brush = SelectObject(hdc, hbrush.into()); let hpen = CreatePen(PS_NULL, 0, COLORREF(0)); - let old_pen = SelectObject(hdc, hpen); + let old_pen = SelectObject(hdc, hpen.into()); let _ = Ellipse(hdc, 0, 0, size, size); let hfont = CreateFontW( - 11, 0, 0, 0, FW_BOLD.0 as i32, 0, 0, 0, - DEFAULT_CHARSET.0 as u32, - OUT_DEFAULT_PRECIS.0 as u32, - CLIP_DEFAULT_PRECIS.0 as u32, - DEFAULT_QUALITY.0 as u32, - (DEFAULT_PITCH.0 | FF_DONTCARE.0) as u32, + 11, + 0, + 0, + 0, + FW_BOLD.0 as i32, + 0, + 0, + 0, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + FONT_PITCH_AND_FAMILY(DEFAULT_PITCH.0 | FF_DONTCARE.0), windows::core::w!("Segoe UI"), ); - let old_font = SelectObject(hdc, hfont); + let old_font = SelectObject(hdc, hfont.into()); SetTextColor(hdc, COLORREF(0x00FFFFFF)); let _ = SetBkMode(hdc, TRANSPARENT); let mut rect = RECT { left: 0, top: 0, right: size, bottom: size }; + let label_len = label_wide.len() - 1; let _ = DrawTextW( hdc, - &label_wide[..label_wide.len() - 1], + &mut label_wide[..label_len], &mut rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE, ); @@ -179,12 +187,12 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> { SelectObject(hdc, old_pen); SelectObject(hdc, old_font); SelectObject(hdc, old_bm); - let _ = DeleteObject(hbrush); - let _ = DeleteObject(hpen); - let _ = DeleteObject(hfont); + let _ = DeleteObject(hbrush.into()); + let _ = DeleteObject(hpen.into()); + let _ = DeleteObject(hfont.into()); let hbm_mask = CreateBitmap(size, size, 1, 1, None) - .map_err(|e| { let _ = DeleteObject(hbm_color); e.to_string() })?; + .map_err(|e| { let _ = DeleteObject(hbm_color.into()); e.to_string() })?; let icon_info = ICONINFO { fIcon: BOOL(1), @@ -193,13 +201,16 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> { hbmMask: hbm_mask, hbmColor: hbm_color, }; - let hicon = CreateIconIndirect(&icon_info) - .map_err(|e| { let _ = DeleteObject(hbm_color); let _ = DeleteObject(hbm_mask); e.to_string() })?; + let hicon = CreateIconIndirect(&icon_info).map_err(|e| { + let _ = DeleteObject(hbm_color.into()); + let _ = DeleteObject(hbm_mask.into()); + e.to_string() + })?; - let _ = DeleteObject(hbm_color); - let _ = DeleteObject(hbm_mask); + let _ = DeleteObject(hbm_color.into()); + let _ = DeleteObject(hbm_mask.into()); let _ = DeleteDC(hdc); - let _ = ReleaseDC(HWND(std::ptr::null_mut()), hdc_screen); + let _ = ReleaseDC(None, hdc_screen); Some(hicon) } @@ -213,7 +224,7 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> { .map_err(|e| e.to_string())?; taskbar.HrInit().map_err(|e| e.to_string())?; taskbar - .SetOverlayIcon(hwnd, hicon, PCWSTR::null()) + .SetOverlayIcon(hwnd, hicon.unwrap_or_default(), PCWSTR::null()) .map_err(|e| e.to_string())?; if let Some(icon) = hicon { let _ = DestroyIcon(icon);