Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ objc2-quartz-core = "0.3.1"
block2 = "0.6.1"

[target.'cfg(target_os = "windows")'.dependencies]
windows-sys = { version = "0.61", features = ["Win32_UI_WindowsAndMessaging"] }
windows-sys = { version = "0.61", features = ["Win32_Graphics_Gdi", "Win32_UI_WindowsAndMessaging"] }

[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-updater = "2"
Expand Down
6 changes: 6 additions & 0 deletions src-tauri/src/app_bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ pub(crate) fn setup(app: &mut tauri::App) -> Result<(), Box<dyn Error>> {
.get_webview_window("main")
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Failed to get main window"))?;

// Only the webview needs transparency for the UI overlay.
#[cfg(target_os = "windows")]
window
.as_ref()
.set_background_color(Some(tauri::utils::config::Color(0, 0, 0, 0)))?;

#[cfg(any(target_os = "windows", target_os = "linux"))]
{
let _ = window.set_decorations(false);
Expand Down
8 changes: 8 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,14 @@ fn show_main_window(app_handle: &tauri::AppHandle) {
let _ = app_handle_for_thread.run_on_main_thread(move || {
if let Some(window) = app_handle_for_show.get_webview_window(MAIN_WINDOW_LABEL) {
let _ = window.show();
#[cfg(target_os = "windows")]
if let Err(error) = crate::platform::windows::paint_native_window_background(
&window,
None,
window.theme().unwrap_or(tauri::Theme::Dark),
) {
log::warn!("Failed to paint native main window background: {error}");
}
}
});
}
Expand Down
20 changes: 17 additions & 3 deletions src-tauri/src/platform/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,26 @@ impl PlatformIntegration for DefaultPlatformIntegration {

fn apply_window_appearance(
&self,
_window: tauri::Window,
window: tauri::Window,
_compact_mode: bool,
_corner_radius: Option<f64>,
_theme: Option<String>,
theme: Option<String>,
) -> Result<(), String> {
Ok(())
#[cfg(target_os = "windows")]
{
let system_theme = window.theme().unwrap_or(tauri::Theme::Dark);
super::windows::paint_native_window_background(
&window,
theme.as_deref(),
system_theme,
)
}

#[cfg(not(target_os = "windows"))]
{
let _ = (window, theme);
Ok(())
}
}

fn set_window_vibrancy_visible(
Expand Down
92 changes: 90 additions & 2 deletions src-tauri/src/platform/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@
mod imp {
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use std::sync::mpsc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Mutex, OnceLock};
use std::time::{Duration, Instant};
use tauri::{Emitter, Manager};
use tokio::time::sleep;
use windows_sys::Win32::Graphics::Gdi::{
CreateSolidBrush, FillRect, GetDC, InvalidateRect, ReleaseDC, UpdateWindow,
};
use windows_sys::Win32::UI::WindowsAndMessaging::{
GetClientRect, SetClassLongPtrW, GCLP_HBRBACKGROUND,
};
use windows_sys::Win32::UI::WindowsAndMessaging::IsZoomed;

const MAIN_WINDOW_LABEL: &str = "main";
Expand All @@ -17,6 +24,87 @@ mod imp {
const PIP_SCREEN_WIDTH_FACTOR: f64 = 0.30;
const PIP_SCREEN_HEIGHT_FACTOR: f64 = 0.45;
const PIP_MARGIN: i32 = 20;
const LIGHT_BACKGROUND_COLOR: u32 = 0x00f6f6f6;
const DARK_BACKGROUND_COLOR: u32 = 0x000f0f0f;
// Win32 COLORREF uses 0x00BBGGRR, so CSS #1e2227 becomes 0x0027221e.
const GRAPHITE_BACKGROUND_COLOR: u32 = 0x0027221e;
static CURRENT_BACKGROUND_COLOR: AtomicU32 = AtomicU32::new(0);

pub(crate) fn paint_native_window_background<W: HasWindowHandle>(
window: &W,
theme: Option<&str>,
system_theme: tauri::Theme,
) -> Result<(), String> {
let handle = window.window_handle().map_err(|error| error.to_string())?;
let hwnd = match handle.as_raw() {
RawWindowHandle::Win32(handle) => {
handle.hwnd.get() as usize as *mut std::ffi::c_void
}
_ => return Err("Main window does not expose a Win32 window handle".into()),
};

let color = match theme.map(|value| value.trim().to_ascii_lowercase()) {
Some(value) if value == "light" => LIGHT_BACKGROUND_COLOR,
Some(value) if value == "dark" => DARK_BACKGROUND_COLOR,
Some(value) if value == "graphite" => GRAPHITE_BACKGROUND_COLOR,
Some(_) => system_background_color(system_theme),
None => {
let current = CURRENT_BACKGROUND_COLOR.load(Ordering::Acquire);
if current == 0 {
system_background_color(system_theme)
} else {
current
}
}
};
CURRENT_BACKGROUND_COLOR.store(color, Ordering::Release);

let brush = background_brush(color);
if brush.is_null() {
return Err("Failed to create native window background brush".into());
}

let mut client_rect = windows_sys::Win32::Foundation::RECT::default();
unsafe {
// The class owns this process-lifetime brush after assignment.
SetClassLongPtrW(hwnd, GCLP_HBRBACKGROUND, brush as isize);
if GetClientRect(hwnd, &mut client_rect) == 0 {
return Err("Failed to query main window client area".into());
}

let dc = GetDC(hwnd);
if dc.is_null() {
return Err("Failed to acquire main window device context".into());
}
FillRect(dc, &client_rect, brush);
ReleaseDC(hwnd, dc);

InvalidateRect(hwnd, std::ptr::null(), 1);
UpdateWindow(hwnd);
}
Ok(())
}

fn system_background_color(theme: tauri::Theme) -> u32 {
match theme {
tauri::Theme::Light => LIGHT_BACKGROUND_COLOR,
_ => DARK_BACKGROUND_COLOR,
}
}

fn background_brush(color: u32) -> *mut std::ffi::c_void {
static LIGHT_BRUSH: OnceLock<usize> = OnceLock::new();
static DARK_BRUSH: OnceLock<usize> = OnceLock::new();
static GRAPHITE_BRUSH: OnceLock<usize> = OnceLock::new();

let slot = match color {
LIGHT_BACKGROUND_COLOR => &LIGHT_BRUSH,
GRAPHITE_BACKGROUND_COLOR => &GRAPHITE_BRUSH,
_ => &DARK_BRUSH,
};
*slot.get_or_init(|| unsafe { CreateSolidBrush(color) as usize })
as *mut std::ffi::c_void
}

#[derive(Clone, Copy, Debug)]
struct WindowSnapshot {
Expand Down Expand Up @@ -383,6 +471,6 @@ mod imp {

#[cfg(target_os = "windows")]
pub(crate) use imp::{
enforce_native_pip_aspect, is_native_pip_enabled, set_native_pip_enabled,
prepare_window_for_fullscreen, update_native_pip_state,
enforce_native_pip_aspect, is_native_pip_enabled, paint_native_window_background,
prepare_window_for_fullscreen, set_native_pip_enabled, update_native_pip_state,
};
12 changes: 12 additions & 0 deletions src-tauri/tauri.windows.conf.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
{
"app": {
"windows": [
{
"title": "Soia",
"width": 1280,
"height": 720,
"decorations": true,
"transparent": false,
"visible": false
}
]
},
"bundle": {
"fileAssociations": [
{
Expand Down
Loading