Fix Windows badge API for windows-rs 0.61 + add libfuse2 for Linux AppImage
Build Lotus Chat Desktop / prepare (push) Successful in 5s
Build Lotus Chat Desktop / build-linux (push) Failing after 14m42s
Build Lotus Chat Desktop / build-windows (push) Failing after 16m14s
Build Lotus Chat Desktop / update-manifest (push) Has been skipped

- 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<T> 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<HICON>)
- Add libfuse2 to apt-get for Linux AppImage bundler (FUSE mount fallback)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 10:19:25 -04:00
parent 44d75881f9
commit 768c286d4a
2 changed files with 49 additions and 37 deletions
+2 -1
View File
@@ -134,7 +134,8 @@ jobs:
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf \
xdg-utils
xdg-utils \
libfuse2
- uses: dtolnay/rust-toolchain@stable
+47 -36
View File
@@ -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<HICON> = if count > 0 {
let label = if count > 99 {
"99+".to_string()
} else {
count.to_string()
};
let label_wide: Vec<u16> = label.encode_utf16().chain(std::iter::once(0)).collect();
let mut label_wide: Vec<u16> = 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);