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
28 changes: 19 additions & 9 deletions crates/winisland-platform-windows/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use windows::Win32::Graphics::Dxgi::{
};
use windows::Win32::Graphics::Gdi::{
EnumDisplayMonitors, GetMonitorInfoW, HDC, HMONITOR, MONITOR_DEFAULTTONEAREST, MONITORINFO,
MonitorFromWindow,
MonitorFromRect, MonitorFromWindow,
};
use windows::Win32::UI::Shell::{QUNS_RUNNING_D3D_FULL_SCREEN, SHQueryUserNotificationState};
use windows::Win32::UI::WindowsAndMessaging::{
CURSORINFO, FindWindowW, GetClassNameW, GetCursorInfo, GetCursorPos, GetForegroundWindow,
GetWindowRect, GetWindowThreadProcessId, IsIconic, SW_RESTORE, SetForegroundWindow, ShowWindow,
Expand Down Expand Up @@ -63,6 +64,12 @@ impl DisplayProvider for WindowsDisplay {
foreground_fullscreen(target)
}

fn exclusive_fullscreen_active(&self) -> bool {
// SAFETY: The query has no inputs and returns the state by value.
unsafe { SHQueryUserNotificationState() }
.is_ok_and(|state| state == QUNS_RUNNING_D3D_FULL_SCREEN)
}

fn cursor_hidden(&self) -> bool {
let mut info = CURSORINFO {
cbSize: size_of::<CURSORINFO>() as u32,
Expand Down Expand Up @@ -170,14 +177,17 @@ fn foreground_fullscreen(target: Rect) -> bool {
&& window_rect.top <= bounds.top
&& window_rect.right >= bounds.right
&& window_rect.bottom >= bounds.bottom;
let target_width = target.right.saturating_sub(target.left);
let target_height = target.bottom.saturating_sub(target.top);
let same_monitor = target_width == 0
|| target_height == 0
|| (bounds.left == target.left
&& bounds.top == target.top
&& bounds.right == target.right
&& bounds.bottom == target.bottom);
let same_monitor = target.right <= target.left
|| target.bottom <= target.top
|| MonitorFromRect(
&RECT {
left: target.left,
top: target.top,
right: target.right,
bottom: target.bottom,
},
MONITOR_DEFAULTTONEAREST,
) == monitor;
fullscreen && same_monitor
}
}
Expand Down
11 changes: 10 additions & 1 deletion crates/winisland-platform-windows/src/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ struct WindowRecord {
window_backdrop: Option<Rc<WindowBackdrop>>,
input: Option<Rc<hit_test::InputWindow>>,
backdrop_capture: Option<Rc<BackdropCapture>>,
skip_taskbar: Option<bool>,
window: Arc<Window>,
backdrop: Option<Arc<Window>>,
}
Expand Down Expand Up @@ -97,6 +98,7 @@ fn register_window(
window_backdrop: None,
input,
backdrop_capture: None,
skip_taskbar: None,
window,
backdrop,
};
Expand Down Expand Up @@ -527,6 +529,11 @@ impl WindowSystem for WindowsWindowSystem {
}

fn apply_overlay_styles(&self, id: WindowId, styles: OverlayStyles) {
let skip_taskbar_changed = WINDOWS.with(|windows| {
windows.borrow_mut().get_mut(&id).is_some_and(|record| {
record.skip_taskbar.replace(styles.skip_taskbar) != Some(styles.skip_taskbar)
})
});
let keyboard = WINDOWS.with(|windows| {
windows
.borrow()
Expand All @@ -549,7 +556,9 @@ impl WindowSystem for WindowsWindowSystem {
styles::set_keyboard_input(HWND(hwnd as *mut _), true);
}
}
window.set_skip_taskbar(styles.skip_taskbar);
if skip_taskbar_changed {
window.set_skip_taskbar(styles.skip_taskbar);
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions crates/winisland-platform/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub trait DisplayProvider {
fn cursor_position(&self) -> Result<Option<Point>, PlatformError>;
/// Checks whether a foreign window covers the target; false on lookup failure.
fn foreign_fullscreen_on(&self, target: Rect) -> bool;
/// Reports whether an app holds Direct3D exclusive fullscreen; false on lookup failure.
fn exclusive_fullscreen_active(&self) -> bool;
/// Reports whether the system cursor is hidden; false on lookup failure.
fn cursor_hidden(&self) -> bool;
/// Starts WMI brightness tracking on a worker, or returns an unavailable error.
Expand Down
2 changes: 1 addition & 1 deletion src/ui/expanded/timer_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ pub fn clear_selection() {
}

pub fn duration_label(seconds: u64) -> String {
if seconds % 60 == 0 {
if seconds.is_multiple_of(60) {
tr_args("timer_minutes", &[&(seconds / 60).to_string()])
} else {
format_remaining(Duration::from_secs(seconds))
Expand Down
4 changes: 3 additions & 1 deletion src/utils/recorder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,9 @@ fn write_job(directory: &Path, job: Job) -> io::Result<()> {
let Job { first, last, frame } = job;
let rgb: Vec<u8> = frame
.pixels
.chunks_exact(4)
.as_chunks::<4>()
.0
.iter()
.flat_map(|pixel| [pixel[0], pixel[1], pixel[2]])
.collect();
let path = frame_path(directory, first);
Expand Down
4 changes: 4 additions & 0 deletions src/window/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ pub struct App {
fullscreen_check: Every,
config_check: Every,
monitor_check: Every,
display_settle_until: Option<Instant>,
display_transition: bool,
working_set_trim: Every,
compact_widget_refresh: Cooldown,
last_config_modified: Option<SystemTime>,
Expand Down Expand Up @@ -277,6 +279,8 @@ impl Default for App {
fullscreen_check: Every::millis(100),
config_check: Every::millis(500),
monitor_check: Every::secs(1),
display_settle_until: None,
display_transition: false,
working_set_trim: Every::new(frame::WORKING_SET_TRIM_INTERVAL),
compact_widget_refresh: Cooldown::ready(),
last_config_modified,
Expand Down
1 change: 1 addition & 0 deletions src/window/app/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ impl App {
&& !self.is_right_dragging
&& self.hide.origin.is_none()
&& self.geom.position_restore_after.is_none()
&& !self.update_display_transition(&win, Instant::now())
&& (position.x != self.geom.configured_x
|| position.y != self.geom.configured_y)
{
Expand Down
11 changes: 6 additions & 5 deletions src/window/app/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,11 @@ impl App {
if now < self.next_frame_deadline {
return;
}
if self
.geom
.position_restore_after
.is_some_and(|restore_after| now >= restore_after)
if !self.display_transition
&& self
.geom
.position_restore_after
.is_some_and(|restore_after| now >= restore_after)
{
self.geom.position_restore_after = None;
self.set_configured_window_position(
Expand All @@ -70,7 +71,7 @@ impl App {
self.geom.configured_y,
);
}
if self.topmost_check.due(now) {
if self.topmost_check.due(now) && !self.update_display_transition(&window, now) {
Self::enforce_overlay_window(&window);
}
self.handle_tray_events(&window);
Expand Down
23 changes: 22 additions & 1 deletion src/window/app/layout.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::time::Duration;
use std::time::{Duration, Instant};

use crate::platform::{MonitorRef, WindowRef, window};
use winisland_core::config::{
Expand All @@ -9,6 +9,8 @@ use winisland_render::text::FontManager;

use super::{App, DEFAULT_ANIMATION_REFRESH_RATE_MILLIHERTZ, IslandLayout};

pub(super) const DISPLAY_SETTLE: Duration = Duration::from_millis(2500);

impl App {
pub(super) fn required_window_size(&self) -> WindowSize {
let compact_scale = self.config.compact_scale;
Expand Down Expand Up @@ -519,3 +521,22 @@ impl App {
}
}
}

impl App {
pub(super) fn update_display_transition(&mut self, window: &WindowRef, now: Instant) -> bool {
if let Some(monitor) = Self::get_target_monitor(window, self.config.monitor_index) {
let size = monitor.size();
let position = monitor.position();
if size.width > 0
&& size.height > 0
&& ((size.width, size.height) != self.geom.monitor_size
|| (position.x, position.y) != self.geom.monitor_pos)
{
self.display_settle_until = Some(now + DISPLAY_SETTLE);
}
}
self.display_transition = self.display_settle_until.is_some_and(|until| now < until)
|| crate::platform::display().exclusive_fullscreen_active();
self.display_transition
}
}
7 changes: 5 additions & 2 deletions src/window/app/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,8 +742,11 @@ impl App {
self.geom.monitor_size = cur_mon_size;
self.geom.monitor_pos = cur_mon_pos;
let (position_x, position_y) = self.compute_window_position(mon_pos, mon_size);
self.set_configured_window_position(window_ref, position_x, position_y);
self.geom.position_restore_after = Some(now + Duration::from_millis(750));
self.geom.configured_x = position_x;
self.geom.configured_y = position_y;
self.display_settle_until = Some(now + super::layout::DISPLAY_SETTLE);
self.display_transition = true;
self.geom.position_restore_after = Some(now + super::layout::DISPLAY_SETTLE);
}
}
}
Expand Down
Loading