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
4 changes: 4 additions & 0 deletions desktop/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,10 @@ impl ApplicationHandler for App {

let Some(render_state) = &mut self.render_state else { return };
if let Some(window) = &self.window {
if !window.can_render() {
return;
}

match render_state.render(window) {
Ok(_) => {}
Err(RenderError::OutdatedUITextureError) => {
Expand Down
7 changes: 7 additions & 0 deletions desktop/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub(crate) trait NativeWindow {
fn init() {}
fn configure(attributes: WindowAttributes, event_loop: &dyn ActiveEventLoop) -> WindowAttributes;
fn new(window: &dyn WinitWindow, app_event_scheduler: AppEventScheduler) -> Self;
fn can_render(&self) -> bool {
true
}
fn update_menu(&self, _entries: Vec<MenuItem>) {}
fn hide(&self) {}
fn hide_others(&self) {}
Expand Down Expand Up @@ -85,6 +88,10 @@ impl Window {
self.winit_window.pre_present_notify();
}

pub(crate) fn can_render(&self) -> bool {
self.native_handle.can_render()
}

pub(crate) fn surface_size(&self) -> winit::dpi::PhysicalSize<u32> {
self.winit_window.surface_size()
}
Expand Down
4 changes: 4 additions & 0 deletions desktop/src/window/win.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ impl super::NativeWindow for NativeWindowImpl {
let native_handle = native_handle::NativeWindowHandle::new(window);
NativeWindowImpl { native_handle }
}

fn can_render(&self) -> bool {
self.native_handle.can_render()
}
}

impl Drop for NativeWindowImpl {
Expand Down
41 changes: 38 additions & 3 deletions desktop/src/window/win/native_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
//! - The helper window is a invisible window that never activates, so it doesn't steal focus from the main window.
//! - The main window needs to update the helper window's position and size whenever it moves or resizes.

use std::sync::OnceLock;
use std::sync::{Arc, Mutex, OnceLock};
use std::time::Instant;
use wgpu::rwh::{HasWindowHandle, RawWindowHandle};
use windows::Win32::Foundation::*;
use windows::Win32::Graphics::Dwm::*;
Expand All @@ -21,11 +22,18 @@ use windows::Win32::UI::WindowsAndMessaging::*;
use windows::core::PCWSTR;
use winit::window::Window;

#[derive(Default)]
struct NativeWindowState {
can_render: bool,
can_render_since: Option<Instant>,
}

#[derive(Clone)]
pub(super) struct NativeWindowHandle {
main: HWND,
helper: HWND,
prev_window_message_handler: isize,
state: Arc<Mutex<NativeWindowState>>,
}
impl NativeWindowHandle {
pub(super) fn new(window: &dyn Window) -> NativeWindowHandle {
Expand Down Expand Up @@ -74,6 +82,7 @@ impl NativeWindowHandle {
main,
helper,
prev_window_message_handler,
state: Arc::new(Mutex::new(NativeWindowState::default())),
};
registry::insert(&native_handle);

Expand Down Expand Up @@ -123,6 +132,32 @@ impl NativeWindowHandle {
let _ = unsafe { DestroyWindow(self.helper) };
}
}

// Rendering should be disabled when window is minimized
// Rendering also needs to be disabled during minimize and restore animations
// Reenabling rendering is done after a small delay to account for restore animation
// TODO: Find a cleaner solution that doesn't depend on a timeout
pub(super) fn can_render(&self) -> bool {
let can_render = !unsafe { IsIconic(self.main).into() } && unsafe { IsWindowVisible(self.main).into() };
let Ok(mut state) = self.state.lock() else {
tracing::error!("Failed to lock NativeWindowState");
return true;
};
match (can_render, state.can_render, state.can_render_since) {
(true, false, None) => {
state.can_render_since = Some(Instant::now());
}
(true, false, Some(can_render_since)) if can_render_since.elapsed().as_millis() > 50 => {
state.can_render = true;
state.can_render_since = None;
}
(false, true, _) => {
state.can_render = false;
}
_ => {}
}
state.can_render
}
}

mod registry {
Expand Down Expand Up @@ -226,7 +261,7 @@ unsafe extern "system" fn main_window_handle_message(hwnd: HWND, msg: u32, wpara
// Call the previous window message handler, this is a standard subclassing pattern.
let prev_window_message_handler_fn_ptr: *const () = std::ptr::without_provenance(handle.prev_window_message_handler as usize);
let prev_window_message_handler_fn = unsafe { std::mem::transmute::<_, _>(prev_window_message_handler_fn_ptr) };
return unsafe { CallWindowProcW(Some(prev_window_message_handler_fn), hwnd, msg, wparam, lparam) };
unsafe { CallWindowProcW(Some(prev_window_message_handler_fn), hwnd, msg, wparam, lparam) }
}

// Helper window message handler, called on the UI thread for every message the helper window receives.
Expand Down Expand Up @@ -290,7 +325,7 @@ unsafe fn position_helper(main: HWND, helper: HWND) {
let w = (r.right - r.left) + RESIZE_BAND_THICKNESS * 2;
let h = (r.bottom - r.top) + RESIZE_BAND_THICKNESS * 2;

let _ = unsafe { SetWindowPos(helper, main, x, y, w, h, SWP_NOACTIVATE) };
let _ = unsafe { SetWindowPos(helper, main, x, y, w, h, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING) };
}

unsafe fn calculate_hit(helper: HWND, lparam: LPARAM) -> u32 {
Expand Down