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 \ libayatana-appindicator3-dev \
librsvg2-dev \ librsvg2-dev \
patchelf \ patchelf \
xdg-utils xdg-utils \
libfuse2
- uses: dtolnay/rust-toolchain@stable - 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")] #[cfg(target_os = "windows")]
{ {
use windows::{ use windows::{
core::PCWSTR, core::{BOOL, PCWSTR},
Win32::{ Win32::{
Foundation::{BOOL, COLORREF, HWND, RECT}, Foundation::{COLORREF, HWND, RECT},
Graphics::Gdi::{ Graphics::Gdi::{
CreateCompatibleDC, CreateDIBSection, CreatePen, CreateSolidBrush, CreateBitmap, CreateCompatibleDC, CreateDIBSection, CreateFontW, CreatePen,
DeleteDC, DeleteObject, DIB_RGB_COLORS, DrawTextW, Ellipse, CreateSolidBrush, DeleteDC, DeleteObject, DIB_RGB_COLORS, DrawTextW, Ellipse,
ReleaseDC, SelectObject, SetBkMode, SetTextColor, BITMAPINFO, ReleaseDC, SelectObject, SetBkMode, SetTextColor, BITMAPINFO,
BITMAPINFOHEADER, BI_RGB, DT_CENTER, DT_SINGLELINE, DT_VCENTER, BITMAPINFOHEADER, BI_RGB, CLIP_DEFAULT_PRECIS, DEFAULT_CHARSET, DEFAULT_PITCH,
PS_NULL, TRANSPARENT, CreateFontW, DEFAULT_CHARSET, DEFAULT_PITCH, DEFAULT_QUALITY, DT_CENTER, DT_SINGLELINE, DT_VCENTER, FF_DONTCARE,
DEFAULT_QUALITY, CLIP_DEFAULT_PRECIS, FW_BOLD, FF_DONTCARE, FONT_PITCH_AND_FAMILY, FW_BOLD, OUT_DEFAULT_PRECIS, PS_NULL, TRANSPARENT,
OUT_DEFAULT_PRECIS,
}, },
UI::{ UI::{
Shell::{ITaskbarList3, TaskbarList}, Shell::{ITaskbarList3, TaskbarList},
WindowsAndMessaging::{CreateBitmap, CreateIconIndirect, DestroyIcon, ICONINFO}, WindowsAndMessaging::{CreateIconIndirect, DestroyIcon, HICON, ICONINFO},
}, },
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER}, 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 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 { let label = if count > 99 {
"99+".to_string() "99+".to_string()
} else { } else {
count.to_string() 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 { unsafe {
let size = 16i32; let size = 16i32;
let hdc_screen = windows::Win32::Graphics::Gdi::GetDC(HWND(std::ptr::null_mut())); let hdc_screen = windows::Win32::Graphics::Gdi::GetDC(None);
let hdc = CreateCompatibleDC(hdc_screen); let hdc = CreateCompatibleDC(Some(hdc_screen));
let bmi = BITMAPINFO { let bmi = BITMAPINFO {
bmiHeader: BITMAPINFOHEADER { bmiHeader: BITMAPINFOHEADER {
@@ -145,32 +144,41 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> {
..Default::default() ..Default::default()
}; };
let mut bits: *mut std::ffi::c_void = std::ptr::null_mut(); 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) let hbm_color =
.map_err(|e| e.to_string())?; CreateDIBSection(Some(hdc), &bmi, DIB_RGB_COLORS, &mut bits, None, 0)
let old_bm = SelectObject(hdc, hbm_color); .map_err(|e| e.to_string())?;
let old_bm = SelectObject(hdc, hbm_color.into());
let hbrush = CreateSolidBrush(COLORREF(0x003030DD)); 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 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 _ = Ellipse(hdc, 0, 0, size, size);
let hfont = CreateFontW( let hfont = CreateFontW(
11, 0, 0, 0, FW_BOLD.0 as i32, 0, 0, 0, 11,
DEFAULT_CHARSET.0 as u32, 0,
OUT_DEFAULT_PRECIS.0 as u32, 0,
CLIP_DEFAULT_PRECIS.0 as u32, 0,
DEFAULT_QUALITY.0 as u32, FW_BOLD.0 as i32,
(DEFAULT_PITCH.0 | FF_DONTCARE.0) as u32, 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"), windows::core::w!("Segoe UI"),
); );
let old_font = SelectObject(hdc, hfont); let old_font = SelectObject(hdc, hfont.into());
SetTextColor(hdc, COLORREF(0x00FFFFFF)); SetTextColor(hdc, COLORREF(0x00FFFFFF));
let _ = SetBkMode(hdc, TRANSPARENT); let _ = SetBkMode(hdc, TRANSPARENT);
let mut rect = RECT { left: 0, top: 0, right: size, bottom: size }; let mut rect = RECT { left: 0, top: 0, right: size, bottom: size };
let label_len = label_wide.len() - 1;
let _ = DrawTextW( let _ = DrawTextW(
hdc, hdc,
&label_wide[..label_wide.len() - 1], &mut label_wide[..label_len],
&mut rect, &mut rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE, 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_pen);
SelectObject(hdc, old_font); SelectObject(hdc, old_font);
SelectObject(hdc, old_bm); SelectObject(hdc, old_bm);
let _ = DeleteObject(hbrush); let _ = DeleteObject(hbrush.into());
let _ = DeleteObject(hpen); let _ = DeleteObject(hpen.into());
let _ = DeleteObject(hfont); let _ = DeleteObject(hfont.into());
let hbm_mask = CreateBitmap(size, size, 1, 1, None) 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 { let icon_info = ICONINFO {
fIcon: BOOL(1), fIcon: BOOL(1),
@@ -193,13 +201,16 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> {
hbmMask: hbm_mask, hbmMask: hbm_mask,
hbmColor: hbm_color, hbmColor: hbm_color,
}; };
let hicon = CreateIconIndirect(&icon_info) let hicon = CreateIconIndirect(&icon_info).map_err(|e| {
.map_err(|e| { let _ = DeleteObject(hbm_color); let _ = DeleteObject(hbm_mask); e.to_string() })?; let _ = DeleteObject(hbm_color.into());
let _ = DeleteObject(hbm_mask.into());
e.to_string()
})?;
let _ = DeleteObject(hbm_color); let _ = DeleteObject(hbm_color.into());
let _ = DeleteObject(hbm_mask); let _ = DeleteObject(hbm_mask.into());
let _ = DeleteDC(hdc); let _ = DeleteDC(hdc);
let _ = ReleaseDC(HWND(std::ptr::null_mut()), hdc_screen); let _ = ReleaseDC(None, hdc_screen);
Some(hicon) Some(hicon)
} }
@@ -213,7 +224,7 @@ fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> {
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
taskbar.HrInit().map_err(|e| e.to_string())?; taskbar.HrInit().map_err(|e| e.to_string())?;
taskbar taskbar
.SetOverlayIcon(hwnd, hicon, PCWSTR::null()) .SetOverlayIcon(hwnd, hicon.unwrap_or_default(), PCWSTR::null())
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
if let Some(icon) = hicon { if let Some(icon) = hicon {
let _ = DestroyIcon(icon); let _ = DestroyIcon(icon);