use super::*;
use crate::{AspectRatio, LogicalSize, MimeType, TextPurpose};
use crcbl_core::KeyCode;
use crcbl_core::input::{ButtonState, Keysym, PointerButton, Scancode, ScrollDelta};
use std::path::PathBuf;
use std::time::Instant;
use super::super::ffi::{DropFiles, Lparam, MinMaxInfo, Point, Rect, msg};
const VK_W: usize = 0x57;
const VK_UP: usize = 0x26;
mod desktop;
use desktop::shell;
#[cfg(feature = "accesskit")]
mod accessibility;
fn window(shell: &mut Win32Shell) -> WindowId {
shell
.create_window(&WindowDesc {
title: "crcbl P5C W2",
..WindowDesc::default()
})
.expect("creating a top-level window")
}
fn hwnd_of(shell: &Win32Shell, window: WindowId) -> Handle {
shell.window(window).expect("a live window").raw()
}
fn send_key(
hwnd: Handle,
message: u32,
virtual_key: usize,
scancode: isize,
extended: bool,
previous: bool,
) {
let mut l_param = (scancode << 16) | 1;
if extended {
l_param |= 1 << 24;
}
if previous {
l_param |= 1 << 30;
}
if message == msg::KEY_UP {
l_param |= 1 << 31;
}
unsafe { ffi::SendMessageW(hwnd, message, virtual_key, l_param) };
}
fn send_mouse(hwnd: Handle, message: u32, w_param: usize, x: i32, y: i32) {
let l_param = (((y as u16 as usize) << 16) | (x as u16 as usize)) as Lparam;
unsafe { ffi::SendMessageW(hwnd, message, w_param, l_param) };
}
fn send_leave(hwnd: Handle) {
unsafe { ffi::SendMessageW(hwnd, msg::MOUSE_LEAVE, 0, 0) };
}
fn make_foreground(hwnd: Handle) -> bool {
use windows::Win32::UI::Input::KeyboardAndMouse::{
INPUT, INPUT_0, INPUT_MOUSE, MOUSEEVENTF_MOVE, MOUSEINPUT, SendInput,
};
let nudge = INPUT {
r#type: INPUT_MOUSE,
Anonymous: INPUT_0 {
mi: MOUSEINPUT {
dwFlags: MOUSEEVENTF_MOVE,
..MOUSEINPUT::default()
},
},
};
unsafe { SendInput(&[nudge], size_of::<INPUT>() as i32) };
unsafe { ffi::SetForegroundWindow(hwnd) != 0 }
}
fn is_foreground(hwnd: Handle) -> bool {
core::ptr::eq(unsafe { ffi::GetForegroundWindow() }, hwnd)
}
fn focus_and_confirm(shell: &mut Win32Shell, window: WindowId, hwnd: Handle) {
const ATTEMPTS: u32 = 8;
const BACKOFF: Duration = Duration::from_millis(10);
const BACKOFF_DOUBLINGS: u32 = 4;
let mut granted = false;
for attempt in 0..ATTEMPTS {
granted = make_foreground(hwnd);
shell.pump(&mut |_| {});
if granted && is_foreground(hwnd) {
send_focus(hwnd, true);
shell.pump(&mut |_| {});
if shell.window_state(window).expect("live").focused && is_foreground(hwnd) {
return;
}
}
std::thread::sleep(BACKOFF * (1 << attempt.min(BACKOFF_DOUBLINGS)));
}
let foreground = unsafe { ffi::GetForegroundWindow() };
panic!(
"the window would not keep the keyboard over {ATTEMPTS} attempts, so nothing \
below this can be asked about a focused window (last SetForegroundWindow \
granted: {granted}; foreground window {foreground:?}, ours {hwnd:?})"
);
}
fn send_focus(hwnd: Handle, focused: bool) {
let message = if focused {
msg::SET_FOCUS
} else {
msg::KILL_FOCUS
};
unsafe { ffi::SendMessageW(hwnd, message, 0, 0) };
}
fn pump_keys(
shell: &mut Win32Shell,
) -> Vec<(Scancode, Option<KeyCode>, Keysym, ButtonState, bool)> {
let mut keys = Vec::new();
shell.pump(&mut |event| {
if let ShellEvent::Key {
scancode,
key_code,
keysym,
state,
repeat,
..
} = event
{
keys.push((scancode, key_code, keysym, state, repeat));
}
});
keys
}
fn clip_rect() -> Rect {
let mut rect = Rect::default();
unsafe { ffi::GetClipCursor(&raw mut rect) };
rect
}
fn virtual_screen() -> Rect {
unsafe {
let left = ffi::GetSystemMetrics(value::SM_X_VIRTUAL_SCREEN);
let top = ffi::GetSystemMetrics(value::SM_Y_VIRTUAL_SCREEN);
Rect {
left,
top,
right: left + ffi::GetSystemMetrics(value::SM_CX_VIRTUAL_SCREEN),
bottom: top + ffi::GetSystemMetrics(value::SM_CY_VIRTUAL_SCREEN),
}
}
}
fn confined_to_client(hwnd: Handle) -> Rect {
super::input::client_screen_rect(hwnd)
.expect("a live window has a client rectangle")
.intersect(virtual_screen())
}
fn clipboard_held_by(hwnd: *mut core::ffi::c_void) -> bool {
core::ptr::eq(unsafe { ffi::GetOpenClipboardWindow() }, hwnd)
}
fn drop_window(shell: &mut Win32Shell) -> WindowId {
shell
.create_window(&WindowDesc {
title: "crcbl P5C W3 — drops",
accept_drops: true,
..WindowDesc::default()
})
.expect("creating a drop target")
}
fn make_hdrop(paths: &[&str], x: i32, y: i32) -> Handle {
let mut list: Vec<u16> = Vec::new();
for path in paths {
list.extend(path.encode_utf16());
list.push(0);
}
list.push(0);
let header = size_of::<DropFiles>();
let bytes = header + list.len() * 2;
let mem = unsafe { ffi::GlobalAlloc(value::GMEM_MOVEABLE, bytes) };
assert!(!mem.is_null(), "GlobalAlloc for a synthetic HDROP");
let locked = unsafe { ffi::GlobalLock(mem) };
assert!(!locked.is_null(), "GlobalLock of a synthetic HDROP");
unsafe {
ptr::write_bytes(locked.cast::<u8>(), 0, bytes);
locked.cast::<DropFiles>().write(DropFiles {
p_files: header as u32,
pt: Point { x, y },
f_nc: 0,
f_wide: 1,
});
ptr::copy_nonoverlapping(
list.as_ptr(),
locked.cast::<u8>().add(header).cast::<u16>(),
list.len(),
);
ffi::GlobalUnlock(mem);
}
mem
}
fn send_drop(hwnd: Handle, paths: &[&str], x: i32, y: i32) {
let hdrop = make_hdrop(paths, x, y);
unsafe { ffi::SendMessageW(hwnd, msg::DROP_FILES, hdrop as usize, 0) };
}
fn pump_drops(shell: &mut Win32Shell) -> Vec<(WindowId, PathBuf, Option<PhysicalPoint>)> {
let mut dropped = Vec::new();
shell.pump(&mut |event| {
if let ShellEvent::DroppedFile {
window,
path,
position,
..
} = event
{
dropped.push((window, path, position));
}
});
dropped
}
fn paste(shell: &mut Win32Shell, window: WindowId, requested: MimeType) -> ClipboardContent {
let request = shell
.clipboard_request(window, requested)
.expect("CLIPBOARD is claimed");
let mut answers = Vec::new();
shell.pump(&mut |event| {
if let ShellEvent::ClipboardData {
window: asked,
request: answered,
mime,
content,
} = event
{
assert_eq!(asked, window, "the answer names the window that asked");
assert_eq!(answered, request, "and the request it answers");
assert!(
mime.matches(requested),
"a Win32 format is a number, so the answer echoes the request: {mime}"
);
answers.push(content);
}
});
assert_eq!(
answers.len(),
1,
"exactly one answer per accepted request, on the first pump: {answers:?}"
);
answers.remove(0)
}
fn cursor_display_count() -> i32 {
unsafe {
let raised = ffi::ShowCursor(1);
ffi::ShowCursor(0);
raised - 1
}
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_window_has_no_size_until_the_first_pump_and_exactly_one_is_enough() {
let mut shell = shell();
let window = window(&mut shell);
assert_eq!(
shell.window_state(window).expect("live").size(),
None,
"a freshly created window has not been configured yet"
);
let mut events = Vec::new();
shell.pump(&mut |event| events.push(event));
let size = shell
.window_state(window)
.expect("live")
.size()
.expect("one pump is enough on Win32");
assert!(!size.is_empty(), "{size}");
assert!(
events.iter().any(|event| matches!(
event,
ShellEvent::Resized { window: reported, .. } if *reported == window
)),
"the size arrives as an event, not only as state: {events:?}"
);
let mut again = Vec::new();
shell.pump(&mut |event| again.push(event));
assert!(
!again
.iter()
.any(|event| matches!(event, ShellEvent::Resized { .. })),
"{again:?}"
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_window_opens_flips_to_borderless_and_back_and_is_destroyed() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let windowed = shell
.window_state(window)
.expect("live")
.size()
.expect("configured");
let primary = shell
.monitors()
.iter()
.find(|monitor| monitor.is_primary)
.expect("a desktop session has a primary monitor")
.id;
let bounds = shell.monitor(primary).expect("just found it").bounds;
shell
.set_mode(
window,
DisplayMode::Borderless {
monitor: Some(primary),
},
)
.expect("borderless on a named monitor is what WINDOW_POSITION claims");
let mut events = Vec::new();
shell.pump(&mut |event| events.push(event));
let state = shell.window_state(window).expect("live");
assert_eq!(
state.size(),
Some(bounds.size()),
"a borderless window covers its monitor exactly"
);
assert!(
state.mode_request_honoured(),
"Win32 has no window manager to refuse: {state:?}"
);
assert!(
events
.iter()
.any(|event| matches!(event, ShellEvent::Resized { .. })),
"the mode change is reported as a resize: {events:?}"
);
shell
.set_mode(window, DisplayMode::Windowed)
.expect("and back");
shell.pump(&mut |_| {});
let state = shell.window_state(window).expect("live");
assert_eq!(
state.size(),
Some(windowed),
"the saved WINDOWPLACEMENT restores the windowed size exactly"
);
assert_eq!(state.effective_mode(), Some(DisplayMode::Windowed));
assert!(state.mode_request_honoured());
shell.destroy_window(window).expect("destroy");
let mut destroyed = false;
shell.pump(&mut |event| {
destroyed |=
matches!(event, ShellEvent::WindowDestroyed { window: gone } if gone == window);
});
assert!(destroyed, "destruction is reported");
assert!(matches!(
shell.window_state(window),
Err(ShellError::InvalidWindow { .. })
));
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_window_created_borderless_can_still_be_made_windowed() {
let mut shell = shell();
let primary = shell
.monitors()
.iter()
.find(|monitor| monitor.is_primary)
.expect("a primary monitor")
.id;
let bounds = shell.monitor(primary).expect("just found it").bounds;
let window = shell
.create_window(&WindowDesc {
title: "crcbl P5C W1 — borderless from the start",
mode: DisplayMode::Borderless {
monitor: Some(primary),
},
size: LogicalSize::new(800.0, 600.0),
..WindowDesc::default()
})
.expect("create borderless");
shell.pump(&mut |_| {});
let state = shell.window_state(window).expect("live");
assert_eq!(state.size(), Some(bounds.size()), "it covers the monitor");
assert!(state.mode_request_honoured());
shell
.set_mode(window, DisplayMode::Windowed)
.expect("and back to a mode it has never been in");
shell.pump(&mut |_| {});
let state = shell.window_state(window).expect("live");
assert_eq!(state.effective_mode(), Some(DisplayMode::Windowed));
let size = state.size().expect("configured");
assert_ne!(size, bounds.size(), "it is no longer the monitor: {size}");
let scale = shell.window(window).expect("live").scale_factor;
assert_eq!(
size,
LogicalSize::new(800.0, 600.0).to_physical(scale),
"the descriptor's windowed size is what it becomes"
);
}
#[test]
fn dead_keys_and_the_input_method_are_set_and_a_held_keyboard_is_not() {
for raw_motion in [false, true] {
let caps = Win32Shell::latch_caps(raw_motion);
assert!(caps.contains(ShellCaps::TEXT_COMPOSE), "TranslateMessage");
assert!(caps.contains(ShellCaps::TEXT_IME), "imm32");
assert!(
!caps.contains(ShellCaps::PANEL_EXCLUSIVE_KEYBOARD),
"nothing takes the foreground back once the user clicks away"
);
}
}
#[test]
fn a_named_cursor_shape_is_applied() {
for raw_motion in [false, true] {
assert!(
Win32Shell::latch_caps(raw_motion).contains(ShellCaps::CURSOR_SHAPE),
"WM_SETCURSOR answers with the shape's stock cursor"
);
}
}
#[test]
fn an_input_region_is_applied() {
for raw_motion in [false, true] {
assert!(
Win32Shell::latch_caps(raw_motion).contains(ShellCaps::INPUT_REGION),
"SetWindowRgn takes the region"
);
}
}
#[test]
fn the_display_can_be_kept_on() {
for raw_motion in [false, true] {
assert!(
Win32Shell::latch_caps(raw_motion).contains(ShellCaps::IDLE_INHIBIT),
"SetThreadExecutionState holds the display"
);
}
}
#[test]
fn a_url_can_be_opened() {
for raw_motion in [false, true] {
assert!(
Win32Shell::latch_caps(raw_motion).contains(ShellCaps::OPEN_URL),
"ShellExecuteW opens the browser"
);
}
}
fn display_required() -> bool {
let previous = unsafe { ffi::SetThreadExecutionState(ffi::value::ES_CONTINUOUS) };
assert_ne!(previous, 0, "SetThreadExecutionState refused");
assert_ne!(unsafe { ffi::SetThreadExecutionState(previous) }, 0);
previous & ffi::value::ES_DISPLAY_REQUIRED != 0
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_destroyed_window_stops_keeping_the_display_on() {
let mut shell = shell();
let window = shell
.create_window(&WindowDesc {
visible: false,
..WindowDesc::default()
})
.expect("create hidden");
assert!(!display_required(), "nothing asked yet");
shell
.set_idle_inhibit(window, true)
.expect("keeping the display on");
assert!(display_required());
shell.destroy_window(window).expect("destroy");
assert!(!display_required(), "the destroyed window let go");
assert!(matches!(
shell.set_idle_inhibit(window, true),
Err(ShellError::InvalidWindow { .. })
));
assert!(!display_required());
}
fn icon_of(hwnd: Handle, slot: usize) -> Handle {
unsafe { ffi::SendMessageW(hwnd, msg::GET_ICON, slot, 0) as Handle }
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_window_takes_the_executable_icon_at_its_dpi() {
let module = super::super::icon::tests::shell32();
let mut shell = shell();
shell.icon_source = unsafe { icon::IconSource::of(module) }.map(Rc::new);
let window = shell
.create_window(&WindowDesc {
visible: false,
..WindowDesc::default()
})
.expect("create hidden");
let hwnd = hwnd_of(&shell, window);
let side = |metric, dpi| {
let side = unsafe { ffi::GetSystemMetricsForDpi(metric, dpi) };
Some((side, side))
};
let pixel_size = super::super::icon::tests::pixel_size;
let dpi = shell.window(window).expect("live").dpi;
let big = icon_of(hwnd, ffi::value::ICON_BIG);
assert_eq!(pixel_size(big), side(ffi::value::SM_CX_ICON, dpi));
assert_eq!(
pixel_size(icon_of(hwnd, ffi::value::ICON_SMALL)),
side(ffi::value::SM_CX_SM_ICON, dpi)
);
let new_dpi = if dpi == 192 { 96 } else { 192 };
let mut rect = Rect::default();
assert_ne!(unsafe { ffi::GetWindowRect(hwnd, &raw mut rect) }, 0);
unsafe {
ffi::SendMessageW(
hwnd,
msg::DPI_CHANGED,
(new_dpi | new_dpi << 16) as usize,
(&raw const rect) as Lparam,
)
};
shell.pump(&mut |_| {});
assert_eq!(shell.window(window).expect("live").dpi, new_dpi);
let replaced = icon_of(hwnd, ffi::value::ICON_BIG);
assert_ne!(replaced, big, "a new icon, not the old one stretched");
assert_eq!(pixel_size(big), None, "the replaced icon was destroyed");
assert_eq!(pixel_size(replaced), side(ffi::value::SM_CX_ICON, new_dpi));
assert_eq!(
pixel_size(icon_of(hwnd, ffi::value::ICON_SMALL)),
side(ffi::value::SM_CX_SM_ICON, new_dpi)
);
shell.destroy_window(window).expect("destroy");
assert_eq!(pixel_size(replaced), None, "destroyed with the window");
drop(shell);
unsafe { ffi::FreeLibrary(module) };
}
#[test]
fn frames_are_not_paced_by_the_compositor() {
for raw_motion in [false, true] {
assert!(!Win32Shell::latch_caps(raw_motion).contains(ShellCaps::FRAME_CALLBACK));
}
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn the_capabilities_are_exactly_what_this_backend_implements() {
let mut shell = shell();
let window = window(&mut shell);
let caps = shell.caps();
for present in [
ShellCaps::MULTI_WINDOW,
ShellCaps::EVENT_WAIT,
ShellCaps::WINDOW_POSITION,
ShellCaps::SERVER_DECORATIONS,
ShellCaps::FRACTIONAL_SCALE,
ShellCaps::ASPECT_HINT_HONORED,
ShellCaps::POINTER_LOCK,
ShellCaps::POINTER_CONFINE,
ShellCaps::POINTER_WARP,
ShellCaps::CLIPBOARD,
ShellCaps::DRAG_DROP,
ShellCaps::TEXT_IME,
ShellCaps::TEXT_COMPOSE,
ShellCaps::TOUCH,
ShellCaps::PANEL,
ShellCaps::PANEL_RESERVE,
ShellCaps::POPUP,
ShellCaps::POPUP_REPOSITION,
ShellCaps::TRANSPARENT,
ShellCaps::CURSOR_SHAPE,
ShellCaps::INPUT_REGION,
ShellCaps::IDLE_INHIBIT,
ShellCaps::OPEN_URL,
] {
assert!(caps.contains(present), "{present:?} is implemented");
}
assert!(
caps.contains(ShellCaps::RAW_POINTER_MOTION),
"RegisterRawInputDevices was refused on this runner, which is a finding"
);
assert!(caps.has_mouselook(), "both halves, which is the point");
assert!(!caps.contains(ShellCaps::HW_UPSCALE));
assert!(!caps.contains(ShellCaps::PANEL_EXCLUSIVE_KEYBOARD));
assert!(!caps.contains(ShellCaps::FRAME_CALLBACK));
assert_eq!(caps, shell.caps(), "latched for the shell's lifetime");
assert!(shell.clipboard_offer(window, &[]).is_ok());
assert!(shell.clipboard_request(window, MimeType::TextUtf8).is_ok());
assert!(
shell.clipboard_readable(window),
"Win32 has no focus gate, so the trait's default is exactly right"
);
for mode in [
PointerMode::Free,
PointerMode::Confined,
PointerMode::Locked,
PointerMode::Free,
] {
assert!(
shell.set_pointer_mode(window, mode).is_ok(),
"{mode:?} is claimed by the capability set"
);
assert_eq!(shell.window_state(window).expect("live").pointer_mode, mode);
}
assert!(shell.warp_pointer(window, PhysicalPoint::ORIGIN).is_ok());
assert!(
shell
.set_cursor(window, Some(CursorIcon::Crosshair))
.is_ok()
);
assert!(shell.set_cursor(window, None).is_ok());
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_key_press_carries_its_position_its_symbol_and_its_repeat_flag() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
send_key(hwnd, msg::KEY_DOWN, VK_W, 0x0011, false, false);
send_key(hwnd, msg::KEY_DOWN, VK_W, 0x0011, false, true);
send_key(hwnd, msg::KEY_UP, VK_W, 0x0011, false, true);
send_key(hwnd, msg::KEY_DOWN, VK_UP, 0x0048, true, false);
let keys = pump_keys(&mut shell);
assert_eq!(keys.len(), 4, "{keys:?}");
let (scancode, key_code, keysym, state, repeat) = keys[0];
assert_eq!(scancode, Scancode(0x0011));
assert_eq!(key_code, Some(KeyCode::KeyW));
assert_eq!(state, ButtonState::Pressed);
assert!(!repeat, "the first press is not a repeat");
assert_eq!(
keysym.to_char(),
Some('w'),
"a US layout labels this key w; a different layout is a different \
letter and still a character"
);
assert!(keys[1].4, "the second press is the system repeating it");
assert_eq!(keys[2].3, ButtonState::Released);
assert!(
!keys[2].4,
"bit 30 is always set on a release and must not be read there"
);
assert_eq!(keys[3].0, Scancode(0xE048), "the E0 prefix is folded in");
assert_eq!(keys[3].1, Some(KeyCode::ArrowUp));
assert_eq!(keys[3].2, Keysym(0xFF52), "XK_Up, from the named table");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_key_injected_with_no_scan_code_is_named_from_its_virtual_key() {
const VK_CONTROL: usize = 0x11;
const VK_ESCAPE: usize = 0x1B;
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
send_key(hwnd, msg::KEY_DOWN, VK_ESCAPE, 0, false, false);
send_key(hwnd, msg::KEY_DOWN, VK_UP, 0, false, false);
send_key(hwnd, msg::KEY_DOWN, VK_CONTROL, 0, true, false);
let keys = pump_keys(&mut shell);
assert_eq!(keys.len(), 3, "{keys:?}");
assert_eq!(keys[0].1, Some(KeyCode::Escape));
assert_eq!(keys[0].0, Scancode(0), "the scan code the message carried");
assert_eq!(keys[1].1, Some(KeyCode::ArrowUp));
assert_eq!(keys[2].1, Some(KeyCode::ControlRight));
assert_eq!(keys[2].0, Scancode(0xE000));
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_key_the_input_method_consumed_reaches_the_consumer_on_neither_edge() {
const VK_BACK: usize = 0x08;
const VK_A: usize = 0x41;
let process = usize::from(value::VK_PROCESS_KEY);
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
send_key(hwnd, msg::KEY_DOWN, process, 0x000E, false, false);
send_key(hwnd, msg::KEY_UP, VK_BACK, 0x000E, false, true);
send_key(hwnd, msg::KEY_DOWN, process, 0x001C, false, false);
send_key(hwnd, msg::KEY_UP, process, 0x001C, false, true);
send_key(hwnd, msg::KEY_DOWN, VK_W, 0x0011, false, false);
let keys = pump_keys(&mut shell);
assert_eq!(keys.len(), 1, "only W is the consumer's: {keys:?}");
assert_eq!(keys[0].1, Some(KeyCode::KeyW));
focus_and_confirm(&mut shell, window, hwnd);
send_key(hwnd, msg::KEY_DOWN, process, 0x001E, false, false);
send_focus(hwnd, false);
shell.pump(&mut |_| {});
send_key(hwnd, msg::KEY_UP, VK_A, 0x001E, false, true);
let keys = pump_keys(&mut shell);
assert_eq!(keys.len(), 1, "the release is reported: {keys:?}");
assert_eq!(keys[0].1, Some(KeyCode::KeyA));
assert_eq!(keys[0].3, ButtonState::Released);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn typing_commits_text_and_a_control_key_commits_nothing() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
for unit in [
u32::from(b'a'),
0x3042, 0xD83C, 0xDFAE, 0x000D, 0x0008, ] {
unsafe { ffi::SendMessageW(hwnd, msg::CHAR, unit as usize, 0) };
}
let mut text = String::new();
shell.pump(&mut |event| {
if let ShellEvent::TextCommit { text: piece, .. } = event {
text.push_str(&piece);
}
});
assert_eq!(
text, "aあ🎮",
"the pair is one character and the controls are not text"
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn the_pointer_enters_moves_clicks_scrolls_and_leaves() {
const ARRIVED_AT: PhysicalPoint = PhysicalPoint::new(40.0, 30.0);
const MOVED_TO: PhysicalPoint = PhysicalPoint::new(41.0, 31.0);
let mut shell = shell();
let window = window(&mut shell);
let hwnd = hwnd_of(&shell, window);
shell.pump(&mut |_| {});
let frame = super::input::client_screen_rect(hwnd).expect("a live window has one");
send_leave(hwnd);
send_mouse(hwnd, msg::MOUSE_MOVE, 0, 40, 30);
send_mouse(hwnd, msg::MOUSE_MOVE, 0, 41, 31);
send_mouse(hwnd, msg::L_BUTTON_DOWN, 0, 41, 31);
send_mouse(hwnd, msg::L_BUTTON_UP, 0, 41, 31);
send_mouse(hwnd, msg::X_BUTTON_DOWN, 1 << 16, 41, 31);
send_mouse(hwnd, msg::X_BUTTON_UP, 1 << 16, 41, 31);
send_mouse(
hwnd,
msg::MOUSE_WHEEL,
(120usize) << 16,
frame.left + 41,
frame.top + 31,
);
send_leave(hwnd);
let mut names = Vec::new();
let mut motions = Vec::new();
let mut buttons = Vec::new();
let mut wheel = None;
let mut crossings: Vec<(bool, Option<PhysicalPoint>, usize)> = Vec::new();
shell.pump(&mut |event| {
names.push(event.name());
match event {
ShellEvent::PointerMotion { abs, raw_delta, .. } => {
motions.push(abs);
assert_eq!(raw_delta, None, "WM_MOUSEMOVE is not raw motion");
}
ShellEvent::Button {
button,
state,
position,
..
} => buttons.push((button, state, position)),
ShellEvent::Wheel {
delta, position, ..
} => wheel = Some((delta, position)),
ShellEvent::PointerFocus {
entered, position, ..
} => crossings.push((entered, position, motions.len())),
_ => {}
}
});
let ours = crossings
.iter()
.position(|(entered, at, _)| *entered && *at == Some(ARRIVED_AT))
.unwrap_or_else(|| {
panic!(
"no arrival carried {ARRIVED_AT:?}, which is where this test's first \
movement put the pointer: {crossings:?} out of {names:?}"
)
});
let derived_from = crossings[ours].2;
assert_eq!(
motions.get(derived_from).copied(),
Some(Some(ARRIVED_AT)),
"an arrival carries the position of the movement it was derived from, and that \
movement is still delivered: motion {derived_from} of {motions:?}. Events {names:?}"
);
assert_eq!(
crossings
.get(ours + 1)
.map(|(entered, at, _)| (*entered, *at)),
Some((false, None)),
"the leave this test sent follows its arrival and carries no position; every \
message between the two is a synchronous SendMessageW, so nothing else can be \
dispatched in there: {crossings:?} out of {names:?}"
);
assert!(
!crossings
.iter()
.any(|(entered, at, _)| *entered && *at == Some(MOVED_TO)),
"the pointer was already inside by the second movement, so {MOVED_TO:?} is a \
movement and not an arrival; announcing it would mean TrackMouseEvent is re-armed \
per message rather than per transition: {crossings:?} out of {names:?}"
);
assert!(motions.contains(&Some(MOVED_TO)), "{motions:?}");
assert_eq!(
buttons,
vec![
(PointerButton::Left, ButtonState::Pressed, Some(MOVED_TO)),
(PointerButton::Left, ButtonState::Released, Some(MOVED_TO)),
(PointerButton::Back, ButtonState::Pressed, Some(MOVED_TO)),
(PointerButton::Back, ButtonState::Released, Some(MOVED_TO)),
],
"{names:?}"
);
let (delta, position) = wheel.expect("the wheel scrolled: {names:?}");
assert_eq!(delta, ScrollDelta::Lines { x: 0.0, y: 1.0 });
assert_eq!(
position,
Some(MOVED_TO),
"the screen coordinates a wheel message carries were converted"
);
}
#[ignore = "clips and warps the real cursor; run-win32-e2e.ps1 runs it"]
#[test]
fn confining_the_pointer_clips_it_and_losing_focus_gives_the_desktop_back() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
let client = super::input::client_screen_rect(hwnd).expect("a live window has one");
let clipped = client.intersect(virtual_screen());
assert_ne!(clip_rect(), clipped, "nothing is clipped to start with");
focus_and_confirm(&mut shell, window, hwnd);
shell
.set_pointer_mode(window, PointerMode::Confined)
.expect("POINTER_CONFINE is claimed");
assert_eq!(clip_rect(), clipped, "the pointer is bounded by the window");
send_focus(hwnd, false);
assert_ne!(clip_rect(), clipped, "focus loss releases the clip");
send_focus(hwnd, true);
assert_eq!(clip_rect(), clipped);
shell.pump(&mut |_| {});
shell
.set_pointer_mode(window, PointerMode::Free)
.expect("start from nothing");
send_focus(hwnd, false);
shell.pump(&mut |_| {});
shell
.set_pointer_mode(window, PointerMode::Confined)
.expect("asking is allowed whether or not it takes effect now");
assert_ne!(
clip_rect(),
clipped,
"an unfocused window does not get the pointer"
);
send_focus(hwnd, true);
assert_eq!(
clip_rect(),
clipped,
"and gets it when the keyboard arrives"
);
shell.pump(&mut |_| {});
shell
.set_pointer_mode(window, PointerMode::Free)
.expect("free always works");
assert_ne!(clip_rect(), clipped);
send_focus(hwnd, true);
assert_ne!(clip_rect(), clipped, "and focus does not resurrect it");
}
#[ignore = "clips and warps the real cursor; run-win32-e2e.ps1 runs it"]
#[test]
fn minimizing_a_captured_window_releases_the_clip() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
focus_and_confirm(&mut shell, window, hwnd);
shell
.set_pointer_mode(window, PointerMode::Confined)
.expect("POINTER_CONFINE is claimed");
let clipped = confined_to_client(hwnd);
assert_eq!(clip_rect(), clipped, "the pointer is bounded by the window");
unsafe { ffi::ShowWindow(hwnd, value::SW_MINIMIZE) };
shell.pump(&mut |_| {});
let released = clip_rect();
assert!(
released.right > released.left && released.bottom > released.top,
"a minimized captured window must not pin the cursor to a point: {released:?}"
);
assert_ne!(
released, clipped,
"the clip is released, not re-applied from the 0×0 client area"
);
unsafe { ffi::ShowWindow(hwnd, value::SW_RESTORE) };
shell.pump(&mut |_| {});
focus_and_confirm(&mut shell, window, hwnd);
let restored = confined_to_client(hwnd);
assert!(
restored.right > restored.left && restored.bottom > restored.top,
"the window did not actually restore, so the comparison below \
would pass on two degenerate rectangles: {restored:?}"
);
assert_eq!(
clip_rect(),
restored,
"restore re-clips from the real client rectangle"
);
shell
.set_pointer_mode(window, PointerMode::Free)
.expect("free always works");
assert_ne!(clip_rect(), clipped);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_second_window_takes_the_pointer_capture_from_the_first() {
let mut shell = shell();
let first = window(&mut shell);
let second = shell
.create_window(&WindowDesc {
title: "crcbl P5C W2 — second",
size: LogicalSize::new(640.0, 480.0),
..WindowDesc::default()
})
.expect("a second window");
shell.pump(&mut |_| {});
shell
.set_pointer_mode(first, PointerMode::Locked)
.expect("lock");
shell
.set_pointer_mode(second, PointerMode::Confined)
.expect("confine");
assert_eq!(
shell.window_state(first).expect("live").pointer_mode,
PointerMode::Free,
"the first window no longer holds anything"
);
assert_eq!(
shell.window_state(second).expect("live").pointer_mode,
PointerMode::Confined
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn hiding_the_cursor_is_balanced_however_many_times_it_is_asked_for() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
focus_and_confirm(&mut shell, window, hwnd);
let baseline = cursor_display_count();
shell.set_cursor(window, None).expect("hide");
assert_eq!(cursor_display_count(), baseline - 1, "one hide is owed");
shell.set_cursor(window, None).expect("hide again");
assert_eq!(
cursor_display_count(),
baseline - 1,
"asking twice must not owe two shows"
);
shell
.set_cursor(window, Some(CursorIcon::Text))
.expect("a shape reveals it again");
assert_eq!(cursor_display_count(), baseline);
shell
.set_cursor(window, Some(CursorIcon::Crosshair))
.expect("and another shape changes nothing about visibility");
assert_eq!(cursor_display_count(), baseline);
shell.set_cursor(window, None).expect("hide");
assert_eq!(cursor_display_count(), baseline - 1);
send_focus(hwnd, false);
shell.pump(&mut |_| {});
assert_eq!(cursor_display_count(), baseline, "focus loss reveals it");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_cursor_shape_is_answered_from_the_window_procedure() {
let mut shell = shell();
let window = window(&mut shell);
shell
.set_cursor(window, Some(CursorIcon::Text))
.expect("an I-beam is a stock Windows cursor");
let hwnd = hwnd_of(&shell, window);
let handled = unsafe {
ffi::SendMessageW(
hwnd,
msg::SET_CURSOR,
hwnd as usize,
value::HT_CLIENT as isize,
)
};
assert_eq!(handled, 1, "the shape was applied, not the class cursor");
const HT_BOTTOM_RIGHT: isize = 17;
let frame = unsafe { ffi::SendMessageW(hwnd, msg::SET_CURSOR, hwnd as usize, HT_BOTTOM_RIGHT) };
assert_ne!(frame, 1, "the non-client area is left to DefWindowProc");
}
#[ignore = "clips and warps the real cursor; run-win32-e2e.ps1 runs it"]
#[test]
fn warping_the_pointer_moves_it_to_a_position_in_the_window() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
let client = super::input::client_screen_rect(hwnd).expect("a live window has one");
focus_and_confirm(&mut shell, window, hwnd);
shell
.warp_pointer(window, PhysicalPoint::new(30.0, 20.0))
.expect("POINTER_WARP is claimed");
let mut point = super::super::ffi::Point::default();
let read = unsafe { ffi::GetCursorPos(&raw mut point) };
assert_ne!(read, 0, "a session with a desktop has a cursor position");
assert_eq!(
(point.x, point.y),
(client.left + 30, client.top + 20),
"the warp landed in screen space at the client offset asked for"
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_decorated_window_really_is_decorated() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let client = shell
.window_state(window)
.expect("live")
.size()
.expect("configured");
let frame = Win32Shell::frame_for(
super::super::geometry::styles(DisplayMode::Windowed, true),
shell.window(window).expect("live").dpi,
);
assert!(frame.height > 0, "a caption has a height: {frame:?}");
assert!(frame.width > 0, "a border has a width: {frame:?}");
assert!(!client.is_empty());
let flush = Win32Shell::frame_for(
super::super::geometry::styles(DisplayMode::Borderless { monitor: None }, true),
shell.window(window).expect("live").dpi,
);
assert_eq!(
flush,
super::super::geometry::Frame::default(),
"WS_POPUP has no frame at all"
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn the_size_constraints_reach_the_window_procedure() {
let mut shell = shell();
let window = window(&mut shell);
shell
.set_constraints(
window,
SizeConstraints {
min: Some(LogicalSize::new(640.0, 480.0)),
max: Some(LogicalSize::new(1920.0, 1080.0)),
aspect: Some(AspectRatio::WIDESCREEN),
},
)
.expect("constraints are a request every backend accepts");
let hwnd = hwnd_of(&shell, window);
let scale = shell.window(window).expect("live").scale_factor;
let mut info = MinMaxInfo::default();
unsafe {
ffi::SendMessageW(
hwnd,
msg::GET_MIN_MAX_INFO,
0,
(&raw mut info) as super::super::ffi::Lparam,
);
}
let min_client_width = (640.0 * scale).round() as i32;
assert!(
info.pt_min_track_size.x >= min_client_width,
"the minimum is the client minimum plus the frame: {info:?}"
);
assert!(
info.pt_max_track_size.x >= (1920.0 * scale).round() as i32,
"{info:?}"
);
let mut rect = Rect {
left: 100,
top: 100,
right: 1100,
bottom: 1100,
};
let handled = unsafe {
ffi::SendMessageW(
hwnd,
msg::SIZING,
super::super::ffi::value::WMSZ_RIGHT,
(&raw mut rect) as super::super::ffi::Lparam,
)
};
assert_eq!(handled, 1, "the rectangle was rewritten");
assert_eq!(rect.width(), 1000, "the dragged edge never moves");
assert!(
rect.height() < 700,
"16:9 makes a 1000-wide window a little over 560 tall: {rect:?}"
);
assert!(rect.height() > 450, "{rect:?}");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_close_request_is_intercepted_and_can_be_refused_then_accepted() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
assert!(matches!(
shell.reply_close_request(window, CloseReply::Keep),
Err(ShellError::NoPendingCloseRequest { .. })
));
let hwnd = hwnd_of(&shell, window);
unsafe { ffi::SendMessageW(hwnd, msg::CLOSE, 0, 0) };
let mut asked = false;
shell.pump(&mut |event| {
asked |= matches!(event, ShellEvent::CloseRequested { window: asked } if asked == window);
});
assert!(asked, "WM_CLOSE is a question, not a notification");
assert!(
shell
.window_state(window)
.expect("still open")
.close_pending
);
shell
.reply_close_request(window, CloseReply::Keep)
.expect("refusing is allowed");
assert!(
shell.window_state(window).is_ok(),
"the window survives a refusal — DefWindowProc would have destroyed it"
);
assert!(!shell.window_state(window).expect("open").close_pending);
unsafe { ffi::SendMessageW(hwnd, msg::CLOSE, 0, 0) };
shell.pump(&mut |_| {});
shell
.reply_close_request(window, CloseReply::Close)
.expect("and accepting closes it");
assert!(matches!(
shell.window_state(window),
Err(ShellError::InvalidWindow { .. })
));
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn both_offered_formats_round_trip_and_the_reader_picks() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
const RON: &str = "(kind:\"node\",id:7,name:\"日本語 🎮\")";
shell
.clipboard_offer(
window,
&[ClipboardOffer::text("hello 🎮"), ClipboardOffer::ron(RON)],
)
.expect("claiming the clipboard needs no user interaction on Win32");
assert!(
!clipboard_held_by(hwnd_of(&shell, window)),
"the guard closed the clipboard on the way out of the offer"
);
assert_eq!(
paste(&mut shell, window, MimeType::TextUtf8).text(),
Some("hello 🎮"),
"an astral codepoint survives the UTF-16 round trip"
);
assert_eq!(
paste(&mut shell, window, MimeType::CrcblRon),
ClipboardContent::Bytes(RON.as_bytes().to_vec()),
"the engine's own format is lossless, padding and all"
);
assert!(
!clipboard_held_by(hwnd_of(&shell, window)),
"and on the way out of every read as well"
);
assert_eq!(
paste(&mut shell, window, MimeType::UriList),
ClipboardContent::Empty
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn an_empty_offer_empties_the_clipboard_and_an_empty_payload_does_not() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
shell
.clipboard_offer(window, &[ClipboardOffer::text("")])
.expect("an empty payload is a payload");
assert_eq!(
paste(&mut shell, window, MimeType::TextUtf8),
ClipboardContent::Bytes(Vec::new())
);
shell
.clipboard_offer(window, &[])
.expect("an empty slice releases");
assert_eq!(
paste(&mut shell, window, MimeType::TextUtf8),
ClipboardContent::Empty
);
assert!(!clipboard_held_by(hwnd_of(&shell, window)));
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_read_is_answered_exactly_once_and_needs_no_second_pump() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
shell
.clipboard_offer(window, &[ClipboardOffer::text("once")])
.expect("publish");
let first = shell
.clipboard_request(window, MimeType::TextUtf8)
.expect("accepted");
let second = shell
.clipboard_request(window, MimeType::CrcblRon)
.expect("accepted");
assert_ne!(first, second, "ids are unique within a shell");
let mut answered = Vec::new();
shell.pump(&mut |event| {
if let ShellEvent::ClipboardData {
request, content, ..
} = event
{
answered.push((request, content));
}
});
assert_eq!(answered.len(), 2, "one each: {answered:?}");
assert_eq!(answered[0].0, first, "in the order they were asked");
assert_eq!(answered[1].0, second);
assert_eq!(answered[0].1.text(), Some("once"));
assert_eq!(
answered[1].1,
ClipboardContent::Empty,
"the RON format was not published"
);
let mut again = Vec::new();
shell.pump(&mut |event| {
if matches!(event, ShellEvent::ClipboardData { .. }) {
again.push(event.name());
}
});
assert!(
again.is_empty(),
"answered once, not once per pump: {again:?}"
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_window_destroyed_before_the_pump_is_not_answered_at_all() {
let mut shell = shell();
let window = window(&mut shell);
let survivor = shell
.create_window(&WindowDesc {
title: "crcbl P5C W3 — survivor",
size: LogicalSize::new(640.0, 480.0),
..WindowDesc::default()
})
.expect("a second window");
shell.pump(&mut |_| {});
shell
.clipboard_offer(window, &[ClipboardOffer::text("gone")])
.expect("publish");
let doomed = shell
.clipboard_request(window, MimeType::TextUtf8)
.expect("accepted while the window was live");
let kept = shell
.clipboard_request(survivor, MimeType::TextUtf8)
.expect("accepted");
shell.destroy_window(window).expect("destroy");
let mut answered = Vec::new();
shell.pump(&mut |event| {
if let ShellEvent::ClipboardData { request, .. } = event {
answered.push(request);
}
});
assert_eq!(
answered,
[kept],
"the dead window's answer went with it and the live one's did not"
);
assert_ne!(doomed, kept);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_drop_becomes_one_event_per_file_at_the_position_it_landed() {
let mut shell = shell();
let window = drop_window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
send_drop(
hwnd,
&[r"C:\assets\My Scene.ron", r"C:\assets\プロジェクト.png"],
12,
34,
);
let dropped = pump_drops(&mut shell);
assert_eq!(dropped.len(), 2, "one event per file: {dropped:?}");
assert_eq!(dropped[0].0, window);
assert_eq!(dropped[0].1, PathBuf::from(r"C:\assets\My Scene.ron"));
assert_eq!(
dropped[1].1,
PathBuf::from(r"C:\assets\プロジェクト.png"),
"a non-ASCII name survives UTF-16 without a lossy step"
);
assert_eq!(
dropped[0].2,
Some(PhysicalPoint::new(12.0, 34.0)),
"the drop point is in client pixels, which is what the seam means"
);
send_drop(hwnd, &[r"C:\a.png"], 1, 2);
send_drop(hwnd, &[r"C:\b.png", r"C:\c.png"], 3, 4);
let batch = pump_drops(&mut shell);
let paths: Vec<PathBuf> = batch.iter().map(|(_, path, _)| path.clone()).collect();
assert_eq!(
paths,
[
PathBuf::from(r"C:\a.png"),
PathBuf::from(r"C:\b.png"),
PathBuf::from(r"C:\c.png")
],
"in order: {batch:?}"
);
assert_eq!(batch[0].2, Some(PhysicalPoint::new(1.0, 2.0)));
assert_eq!(
batch[1].2,
Some(PhysicalPoint::new(3.0, 4.0)),
"the second drop's point, not the first's"
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_window_that_did_not_ask_for_drops_reports_none() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
send_drop(hwnd, &[r"C:\unwanted.png"], 5, 5);
assert!(
pump_drops(&mut shell).is_empty(),
"the descriptor said no, and that is the answer"
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn the_drop_registration_survives_a_trip_through_borderless() {
let mut shell = shell();
let window = drop_window(&mut shell);
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, window);
let accepts = || {
let ex_style = unsafe { ffi::GetWindowLongPtrW(hwnd, value::GWL_EX_STYLE) } as u32;
ex_style & super::super::ffi::style::EX_ACCEPT_FILES
};
assert_ne!(accepts(), 0, "created with drops accepted");
let primary = shell
.monitors()
.iter()
.find(|monitor| monitor.is_primary)
.expect("a primary monitor")
.id;
shell
.set_mode(
window,
DisplayMode::Borderless {
monitor: Some(primary),
},
)
.expect("borderless");
assert_ne!(accepts(), 0, "the style rewrite carried the bit");
shell
.set_mode(window, DisplayMode::Windowed)
.expect("and back");
assert_ne!(accepts(), 0);
shell.pump(&mut |_| {});
send_drop(hwnd, &[r"C:\after.ron"], 7, 8);
let dropped = pump_drops(&mut shell);
assert_eq!(dropped.len(), 1, "{dropped:?}");
assert_eq!(dropped[0].1, PathBuf::from(r"C:\after.ron"));
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn opening_the_clipboard_reports_how_it_went_and_always_closes_it() {
let mut shell = shell();
let window = window(&mut shell);
let hwnd = hwnd_of(&shell, window);
let opened = {
let board = super::super::clipboard::Clipboard::open(hwnd)
.expect("an idle desktop lets a process open the clipboard");
assert!(clipboard_held_by(hwnd), "held for the guard's lifetime");
board.opened()
};
assert!(
!clipboard_held_by(hwnd),
"and given back the moment the guard leaves scope"
);
assert!(
!matches!(opened, Opened::Refused { .. }),
"another process held the clipboard for the whole budget: {opened:?}"
);
if let Opened::After { attempts } = opened {
assert!(
attempts <= super::super::clipboard::OPEN_ATTEMPTS,
"the retry is bounded: {attempts}"
);
}
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn every_method_taking_a_window_rejects_a_stale_handle() {
let mut shell = shell();
let stale = window(&mut shell);
shell.destroy_window(stale).expect("destroy");
let invalid = |result: Result<(), ShellError>| {
assert!(
matches!(result, Err(ShellError::InvalidWindow { .. })),
"{result:?}"
);
};
invalid(shell.window_state(stale).map(|_| ()));
invalid(shell.set_title(stale, "gone"));
invalid(shell.set_visible(stale, true));
invalid(shell.set_mode(stale, DisplayMode::Windowed));
invalid(shell.set_constraints(stale, SizeConstraints::NONE));
invalid(shell.surface_target(stale).map(|_| ()));
invalid(shell.set_cursor(stale, None));
invalid(shell.set_pointer_mode(stale, PointerMode::Free));
invalid(shell.warp_pointer(stale, PhysicalPoint::ORIGIN));
invalid(shell.reply_close_request(stale, CloseReply::Keep));
invalid(shell.clipboard_offer(stale, &[]));
invalid(
shell
.clipboard_request(stale, MimeType::TextUtf8)
.map(|_| ()),
);
invalid(shell.destroy_window(stale));
assert!(!shell.clipboard_readable(stale));
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn two_windows_coexist_and_are_told_apart() {
let mut shell = shell();
let first = window(&mut shell);
let second = shell
.create_window(&WindowDesc {
title: "crcbl P5C W1 — second",
size: LogicalSize::new(640.0, 480.0),
..WindowDesc::default()
})
.expect("a second window");
assert_ne!(first, second);
assert_ne!(hwnd_of(&shell, first), hwnd_of(&shell, second));
let mut resized = Vec::new();
shell.pump(&mut |event| {
if let ShellEvent::Resized { window, size, .. } = event {
resized.push((window, size));
}
});
assert_eq!(resized.len(), 2, "one configuration each: {resized:?}");
let sizes: Vec<PhysicalSize> = resized.iter().map(|(_, size)| *size).collect();
assert_ne!(sizes[0], sizes[1], "and they are not the same window twice");
shell.destroy_window(first).expect("destroy");
assert!(shell.window_state(second).is_ok());
assert!(shell.window_state(first).is_err());
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_short_timed_wait_is_not_rounded_up_to_the_clock_tick() {
const TIMEOUT: Duration = Duration::from_millis(4);
const CEILING: Duration = Duration::from_millis(10);
const ATTEMPTS: usize = 40;
const SAMPLES: usize = 10;
let mut shell = shell();
let mut slept: Vec<Duration> = (0..ATTEMPTS)
.filter_map(|_| {
let start = std::time::Instant::now();
(shell.wait(Some(TIMEOUT)) == Wake::TimedOut).then(|| start.elapsed())
})
.collect();
assert!(
slept.len() >= SAMPLES,
"only {} of {ATTEMPTS} waits timed out on a thread with no window, too few to measure",
slept.len()
);
slept.sort_unstable();
let median = slept[slept.len() / 2];
assert!(
median < CEILING,
"a {TIMEOUT:?} wait slept a median of {median:?} over {} timed-out waits; a wait \
rounded to the system clock tick sleeps about 15.6 ms",
slept.len()
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_waker_ends_a_wait_from_another_thread_and_rings_coalesce() {
const RING_AFTER: Duration = Duration::from_millis(50);
const LONG: Duration = Duration::from_secs(20);
let mut shell = shell();
let waker = shell.waker().expect("the Win32 backend has a waker");
let ringer = waker.clone();
let thread = std::thread::spawn(move || {
std::thread::sleep(RING_AFTER);
ringer.wake();
});
let woke = shell.wait(Some(LONG));
thread.join().expect("the ringing thread");
assert!(
matches!(woke, Wake::Woken),
"expected the ring, got {woke:?}"
);
for _ in 0..3 {
waker.wake();
}
let first = shell.wait(Some(LONG));
assert!(
matches!(first, Wake::Woken),
"expected the ring, got {first:?}"
);
let second = shell.wait(Some(RING_AFTER));
assert!(
matches!(second, Wake::TimedOut),
"three rings ended a second wait: {second:?}"
);
}
fn app_user_model_id(hwnd: Handle) -> Option<String> {
use windows::Win32::Foundation::HWND;
use windows::Win32::Storage::EnhancedStorage::PKEY_AppUserModel_ID;
use windows::Win32::UI::Shell::PropertiesSystem::{
IPropertyStore, SHGetPropertyStoreForWindow,
};
let store: IPropertyStore =
unsafe { SHGetPropertyStoreForWindow(HWND(hwnd)) }.expect("a window has a property store");
let value = unsafe { store.GetValue(&PKEY_AppUserModel_ID) }.expect("the property reads");
let text = value.to_string();
(!text.is_empty()).then_some(text)
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn app_id_is_the_windows_own_app_user_model_id() {
let mut shell = shell();
let labelled = shell
.create_window(&WindowDesc {
title: "app id",
app_id: "Kryptic.Crcbl.E2E",
..WindowDesc::default()
})
.expect("a window with an app id");
let unlabelled = shell
.create_window(&WindowDesc {
title: "bad app id",
app_id: "Kryptic Crcbl",
..WindowDesc::default()
})
.expect("an app id Windows would refuse does not refuse the window");
assert_eq!(
app_user_model_id(hwnd_of(&shell, labelled)).as_deref(),
Some("Kryptic.Crcbl.E2E")
);
assert_eq!(app_user_model_id(hwnd_of(&shell, unlabelled)), None);
shell.destroy_window(labelled).expect("destroy");
shell.destroy_window(unlabelled).expect("destroy");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn an_event_the_waits_own_drain_records_ends_the_wait() {
const LONG: Duration = Duration::from_secs(20);
let mut shell = shell();
let window = window(&mut shell);
let hwnd = hwnd_of(&shell, window);
shell.pump(&mut |_| {});
let posted = unsafe { ffi::PostMessageW(hwnd, msg::CLOSE, 0, 0) };
assert_ne!(posted, 0, "the close is posted");
let start = Instant::now();
let woke = shell.wait(Some(LONG));
let waited = start.elapsed();
assert_eq!(
woke,
Wake::Recorded,
"the drain recorded the close, so the wait must not sleep (returned after {waited:?})"
);
let mut asked = false;
shell.pump(&mut |event| {
asked |= matches!(event, ShellEvent::CloseRequested { window: w } if w == window);
});
assert!(asked, "the next pump delivers the close request");
shell.destroy_window(window).expect("destroy");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn wait_events_genuinely_blocks() {
let mut shell = shell();
let _window = window(&mut shell);
let mut settled = false;
for _ in 0..16 {
shell.pump(&mut |_| {});
if shell.wait(Some(Duration::from_millis(0))) == Wake::TimedOut {
settled = true;
break;
}
}
const ATTEMPTS: usize = 5;
const TIMEOUT: Duration = Duration::from_millis(50);
const FLOOR: Duration = Duration::from_millis(40);
const BLOCKED: Duration = Duration::from_millis(2);
let mut slept = false;
let mut woken = Vec::new();
for attempt in 0..ATTEMPTS {
let start = Instant::now();
let woke = shell.wait(Some(TIMEOUT));
let waited = start.elapsed();
woken.push(format!(
"attempt {attempt}: {woke:?} after {waited:?}, queue {:#06x}, message {:?}",
Win32Shell::queue_status(),
Win32Shell::peek_pending()
));
slept = match woke {
Wake::TimedOut => waited >= FLOOR,
_ => waited >= BLOCKED,
};
if slept {
break;
}
shell.pump(&mut |_| {});
}
assert!(
slept,
"every one of {ATTEMPTS} waits came back inside {BLOCKED:?} without reaching its \
{TIMEOUT:?} timeout, so this backend's EVENT_WAIT never sleeps at all (the queue \
{}): {woken:#?}",
if settled {
"was drained to quiescence first"
} else {
"never went quiet in 16 drain-and-check rounds, so a return to a full queue is \
expected — but not five in a row, each in microseconds"
}
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn the_monitors_are_enumerated_with_one_primary_and_a_scale_factor() {
let shell = shell();
let monitors = shell.monitors();
assert!(
!monitors.is_empty(),
"a session with a window station has at least one display"
);
assert_eq!(
monitors.iter().filter(|monitor| monitor.is_primary).count(),
1,
"exactly one primary: {monitors:?}"
);
for monitor in monitors {
assert!(!monitor.name.is_empty(), "the device name: {monitor:?}");
assert!(!monitor.size().is_empty(), "{monitor:?}");
assert!(monitor.scale_factor >= 0.5, "{monitor:?}");
assert!(
monitor.work_area.width <= monitor.bounds.width
&& monitor.work_area.height <= monitor.bounds.height,
"the work area is the bounds minus the taskbar: {monitor:?}"
);
assert_eq!(
shell.monitor(monitor.id).map(|found| &found.name),
Some(&monitor.name),
"every id resolves"
);
}
let ids: Vec<MonitorId> = monitors.iter().map(|monitor| monitor.id).collect();
let mut unique = ids.clone();
unique.sort_unstable();
unique.dedup();
assert_eq!(unique.len(), ids.len(), "ids are not reused: {ids:?}");
assert_eq!(shell.monitor(MonitorId(u32::MAX)), None);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn hiding_a_window_is_real_and_reversible() {
let mut shell = shell();
let window = window(&mut shell);
shell.pump(&mut |_| {});
assert!(shell.window_state(window).expect("live").visible);
shell.set_visible(window, false).expect("hide");
assert!(
!shell.window_state(window).expect("live").visible,
"visibility is read back from the system, not assumed from the request"
);
shell.set_visible(window, true).expect("show");
assert!(shell.window_state(window).expect("live").visible);
let hidden = shell
.create_window(&WindowDesc {
visible: false,
..WindowDesc::default()
})
.expect("create hidden");
assert!(!shell.window_state(hidden).expect("live").visible);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn the_title_round_trips_and_a_nul_is_refused() {
let mut shell = shell();
let window = window(&mut shell);
shell
.set_title(window, "crcbl — 日本語 🎮")
.expect("UTF-16");
assert!(matches!(
shell.set_title(window, "save\0nothing"),
Err(ShellError::InvalidDescriptor(_))
));
assert!(matches!(
shell.create_window(&WindowDesc {
app_id: "sh.kryptic\0crcbl",
..WindowDesc::default()
}),
Err(ShellError::InvalidDescriptor(_)),
));
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn the_surface_target_exists_before_the_window_is_configured() {
let mut shell = shell();
let window = window(&mut shell);
assert_eq!(shell.window_state(window).expect("live").size(), None);
let target = shell.surface_target(window).expect("available already");
let SurfaceTarget::Win32 {
hinstance,
hwnd,
transparent,
} = target
else {
panic!("the Win32 backend produces a Win32 target: {target:?}");
};
assert_eq!(hwnd.as_ptr(), hwnd_of(&shell, window));
assert_eq!(hinstance, shell.instance);
assert!(!transparent, "an opaque window presents to its HWND");
shell.pump(&mut |_| {});
assert_eq!(shell.surface_target(window).expect("still live"), target);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn the_backend_reports_itself_and_is_reachable_by_name() {
let shell = shell();
assert_eq!(shell.backend(), ShellBackend::Win32);
assert_eq!(ShellBackend::Win32.as_str(), "win32");
let boxed = crate::open_backend(ShellBackend::Win32).expect("registered on Windows");
assert_eq!(boxed.backend(), ShellBackend::Win32);
if std::env::var(crate::BACKEND_ENV_VAR).is_err() {
let automatic = crate::open().expect("auto-selection has one candidate here");
assert_eq!(automatic.backend(), ShellBackend::Win32);
}
}
fn transparency_of(shell: &Win32Shell, window: WindowId) -> (bool, bool) {
use super::super::ffi::style;
let (_, ex_style, _) = styles_and_rect(hwnd_of(shell, window));
let target = shell.surface_target(window).expect("a live window");
let SurfaceTarget::Win32 { transparent, .. } = target else {
panic!("the Win32 backend produces a Win32 target: {target:?}");
};
(ex_style & style::EX_NO_REDIRECTION_BITMAP != 0, transparent)
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_transparent_window_of_every_role_has_no_redirection_surface() {
let mut shell = shell();
assert!(shell.caps().contains(ShellCaps::TRANSPARENT));
let opaque = window(&mut shell);
assert_eq!(transparency_of(&shell, opaque), (false, false));
let toplevel = shell
.create_window(&WindowDesc {
title: "crcbl transparent toplevel",
transparent: true,
..WindowDesc::default()
})
.expect("a transparent toplevel");
let panel = shell
.create_window(&WindowDesc {
transparent: true,
..panel_desc(
"crcbl transparent launcher",
LogicalSize::new(400.0, 300.0),
crate::PanelDesc {
layer: crate::PanelLayer::Overlay,
keyboard: crate::PanelKeyboard::Exclusive,
..crate::PanelDesc::default()
},
)
})
.expect("a transparent panel");
let popup = shell
.create_window(&WindowDesc {
title: "crcbl transparent popup",
size: LogicalSize::new(120.0, 40.0),
transparent: true,
role: crate::WindowRole::Popup(menu_against(panel, crate::PopupInput::Pointer)),
..WindowDesc::default()
})
.expect("a transparent popup");
shell.pump(&mut |_| {});
for (what, window) in [("toplevel", toplevel), ("panel", panel), ("popup", popup)] {
assert_eq!(transparency_of(&shell, window), (true, true), "{what}");
}
shell
.set_mode(toplevel, DisplayMode::Borderless { monitor: None })
.expect("borderless");
shell.pump(&mut |_| {});
assert_eq!(
transparency_of(&shell, toplevel),
(true, true),
"a style rewrite must not drop the redirection bit"
);
shell
.set_mode(toplevel, DisplayMode::Windowed)
.expect("windowed");
shell.pump(&mut |_| {});
assert_eq!(transparency_of(&shell, toplevel), (true, true), "and back");
for window in [popup, panel, toplevel, opaque] {
shell.destroy_window(window).expect("destroy");
}
}
fn panel_desc<'a>(
title: &'a str,
size: LogicalSize,
panel: crate::PanelDesc<'a>,
) -> WindowDesc<'a> {
WindowDesc {
title,
size,
role: crate::WindowRole::Panel(panel),
..WindowDesc::default()
}
}
fn styles_and_rect(hwnd: Handle) -> (u32, u32, Rect) {
let (window_style, ex_style) = unsafe {
(
ffi::GetWindowLongPtrW(hwnd, value::GWL_STYLE) as u32,
ffi::GetWindowLongPtrW(hwnd, value::GWL_EX_STYLE) as u32,
)
};
let mut rect = Rect::default();
unsafe { ffi::GetWindowRect(hwnd, &raw mut rect) };
(window_style, ex_style, rect)
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_bar_is_a_topmost_tool_window_across_the_top_of_its_monitors_work_area() {
use super::super::ffi::style;
const HEIGHT: f64 = 30.0;
let mut shell = shell();
let primary = shell
.monitors()
.iter()
.find(|monitor| monitor.is_primary)
.cloned()
.expect("a primary monitor");
let bar = shell
.create_window(&panel_desc(
"crcbl panel bar",
LogicalSize::new(0.0, HEIGHT),
crate::PanelDesc {
monitor: Some(primary.id),
anchor: crate::PanelAnchor::TOP
| crate::PanelAnchor::LEFT
| crate::PanelAnchor::RIGHT,
..crate::PanelDesc::default()
},
))
.expect("a panel");
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, bar);
let (window_style, ex_style, rect) = styles_and_rect(hwnd);
assert_ne!(ex_style & style::EX_TOOL_WINDOW, 0, "out of the taskbar");
assert_eq!(ex_style & style::EX_APP_WINDOW, 0);
assert_ne!(ex_style & style::EX_TOPMOST, 0, "above application windows");
assert_ne!(
ex_style & style::EX_NO_ACTIVATE,
0,
"a bar takes no keyboard"
);
assert_ne!(window_style & style::POPUP, 0);
assert_eq!(window_style & style::CAPTION, 0, "no title bar");
let height = LogicalSize::new(0.0, HEIGHT)
.to_physical(primary.scale_factor)
.height;
let area = primary.work_area;
assert_eq!(
shell.window_state(bar).expect("live").size(),
Some(PhysicalSize::new(area.width, height)),
"stretched across the work area, at its own height"
);
assert_eq!(
(rect.left, rect.top, rect.right - rect.left),
(area.x, area.y, area.width as i32),
"held against the top of the work area"
);
let state = shell.window_state(bar).expect("live");
assert_eq!(state.effective_mode(), Some(DisplayMode::Windowed));
shell
.set_mode(bar, DisplayMode::Borderless { monitor: None })
.expect("recorded");
shell.pump(&mut |_| {});
let state = shell.window_state(bar).expect("live");
assert_eq!(state.effective_mode(), Some(DisplayMode::Windowed));
assert!(!state.mode_request_honoured());
let (_, ex_style, _) = styles_and_rect(hwnd);
assert_ne!(ex_style & style::EX_TOOL_WINDOW, 0, "still a panel");
shell.destroy_window(bar).expect("destroy");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_launcher_is_centred_on_the_monitor_it_opens_on_and_can_take_the_keyboard() {
use super::super::ffi::style;
let mut shell = shell();
let launcher = shell
.create_window(&panel_desc(
"crcbl panel launcher",
LogicalSize::new(400.0, 300.0),
crate::PanelDesc {
layer: crate::PanelLayer::Overlay,
keyboard: crate::PanelKeyboard::Exclusive,
..crate::PanelDesc::default()
},
))
.expect("a panel");
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, launcher);
let (_, ex_style, rect) = styles_and_rect(hwnd);
assert_eq!(
ex_style & style::EX_NO_ACTIVATE,
0,
"a panel that takes the keyboard can be activated"
);
assert_ne!(ex_style & style::EX_TOPMOST, 0);
let on = shell.monitor_of(hwnd).expect("on a monitor");
let monitor = shell.monitor(on).cloned().expect("a known monitor");
let size = LogicalSize::new(400.0, 300.0).to_physical(monitor.scale_factor);
assert_eq!(
shell.window_state(launcher).expect("live").size(),
Some(size),
"an unanchored panel is the size it asked for"
);
let area = monitor.work_area;
assert_eq!(
(rect.left, rect.top),
(
area.x + (area.width as i32 - size.width as i32) / 2,
area.y + (area.height as i32 - size.height as i32) / 2,
),
"centred in the work area"
);
shell.destroy_window(launcher).expect("destroy");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_bottom_layer_panel_is_not_topmost() {
use super::super::ffi::style;
let mut shell = shell();
let widget = shell
.create_window(&panel_desc(
"crcbl panel widget",
LogicalSize::new(200.0, 200.0),
crate::PanelDesc {
layer: crate::PanelLayer::Bottom,
anchor: crate::PanelAnchor::TOP | crate::PanelAnchor::RIGHT,
..crate::PanelDesc::default()
},
))
.expect("a panel");
shell.pump(&mut |_| {});
let (_, ex_style, _) = styles_and_rect(hwnd_of(&shell, widget));
assert_eq!(ex_style & style::EX_TOPMOST, 0);
assert_ne!(ex_style & style::EX_TOOL_WINDOW, 0);
shell.destroy_window(widget).expect("destroy");
}
fn work_area_until(shell: &mut Win32Shell, settled: impl Fn(PhysicalRect) -> bool) -> PhysicalRect {
let deadline = Instant::now() + std::time::Duration::from_secs(5);
loop {
let area = shell
.enumerate_monitors()
.into_iter()
.find(|monitor| monitor.is_primary)
.expect("a primary monitor")
.work_area;
if settled(area) || Instant::now() >= deadline {
return area;
}
shell.pump(&mut |_| {});
std::thread::sleep(std::time::Duration::from_millis(20));
}
}
#[test]
#[ignore = "reserves part of the real desktop's work area: run-win32-e2e.ps1 runs it"]
fn a_reserving_bar_is_an_appbar_that_takes_its_edge_and_gives_it_back() {
const HEIGHT: f64 = 30.0;
let mut shell = shell();
let primary = shell
.monitors()
.iter()
.find(|monitor| monitor.is_primary)
.cloned()
.expect("a primary monitor");
let before = work_area_until(&mut shell, |_| true);
let depth = LogicalSize::new(0.0, HEIGHT)
.to_physical(primary.scale_factor)
.height;
let bar = shell
.create_window(&panel_desc(
"crcbl appbar",
LogicalSize::new(0.0, HEIGHT),
crate::PanelDesc {
monitor: Some(primary.id),
anchor: crate::PanelAnchor::TOP
| crate::PanelAnchor::LEFT
| crate::PanelAnchor::RIGHT,
reserve: crate::Reserve::OwnExtent,
..crate::PanelDesc::default()
},
))
.expect("a reserving panel");
shell.pump(&mut |_| {});
let reserved = work_area_until(&mut shell, |area| area.y != before.y);
assert_eq!(
(reserved.y, reserved.height),
(before.y + depth as i32, before.height - depth),
"the work area gave up the bar's height at the top"
);
let (_, _, rect) = styles_and_rect(hwnd_of(&shell, bar));
assert_eq!(
(rect.top, rect.bottom - rect.top),
(before.y, depth as i32),
"the bar sits in the strip it reserved"
);
shell.destroy_window(bar).expect("destroy");
let after = work_area_until(&mut shell, |area| area == before);
assert_eq!(after, before, "destroying the bar gave the edge back");
}
#[test]
#[ignore = "reserves part of the real desktop's work area: run-win32-e2e.ps1 runs it"]
fn a_reconfigured_appbar_reserves_its_new_height_and_gives_it_back_when_told() {
const HEIGHT: f64 = 30.0;
const TALLER: f64 = 50.0;
let mut shell = shell();
let primary = shell
.monitors()
.iter()
.find(|monitor| monitor.is_primary)
.cloned()
.expect("a primary monitor");
let before = work_area_until(&mut shell, |_| true);
let depth = |height: f64| {
LogicalSize::new(0.0, height)
.to_physical(primary.scale_factor)
.height
};
let desc = crate::PanelDesc {
monitor: Some(primary.id),
anchor: crate::PanelAnchor::TOP | crate::PanelAnchor::LEFT | crate::PanelAnchor::RIGHT,
reserve: crate::Reserve::OwnExtent,
..crate::PanelDesc::default()
};
let bar = shell
.create_window(&panel_desc(
"crcbl reconfigured appbar",
LogicalSize::new(0.0, HEIGHT),
desc,
))
.expect("a reserving panel");
shell.pump(&mut |_| {});
work_area_until(&mut shell, |area| area.y == before.y + depth(HEIGHT) as i32);
shell
.set_panel(bar, LogicalSize::new(0.0, TALLER), &desc)
.expect("set_panel");
let reserved = work_area_until(&mut shell, |area| area.y == before.y + depth(TALLER) as i32);
assert_eq!(
(reserved.y, reserved.height),
(
before.y + depth(TALLER) as i32,
before.height - depth(TALLER)
),
"the work area gave up the new height at the top"
);
let (_, _, rect) = styles_and_rect(hwnd_of(&shell, bar));
assert_eq!(
(rect.top, rect.bottom - rect.top),
(before.y, depth(TALLER) as i32),
"the bar sits in the new strip"
);
let mut resized = Vec::new();
shell.pump(&mut |event| {
if let ShellEvent::Resized { window, size, .. } = event
&& window == bar
{
resized.push(size.height);
}
});
assert_eq!(
resized.last(),
Some(&depth(TALLER)),
"the new height arrived as a resize"
);
assert_eq!(
shell
.window_state(bar)
.expect("live")
.size()
.map(|size| size.height),
Some(depth(TALLER))
);
let unreserved = crate::PanelDesc {
reserve: crate::Reserve::None,
..desc
};
shell
.set_panel(bar, LogicalSize::new(0.0, TALLER), &unreserved)
.expect("set_panel");
let after = work_area_until(&mut shell, |area| area == before);
assert_eq!(
after, before,
"a bar that stops reserving gives the edge back"
);
shell.destroy_window(bar).expect("destroy");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_reconfigured_panel_changes_band_and_keyboard_in_place() {
use super::super::ffi::style;
let mut shell = shell();
let size = LogicalSize::new(200.0, 200.0);
let desc = crate::PanelDesc {
anchor: crate::PanelAnchor::TOP | crate::PanelAnchor::RIGHT,
..crate::PanelDesc::default()
};
let widget = shell
.create_window(&panel_desc("crcbl reconfigured panel", size, desc))
.expect("a panel");
shell.pump(&mut |_| {});
let hwnd = hwnd_of(&shell, widget);
let (_, ex_style, _) = styles_and_rect(hwnd);
assert_ne!(ex_style & style::EX_TOPMOST, 0);
assert_ne!(ex_style & style::EX_NO_ACTIVATE, 0);
let lowered = crate::PanelDesc {
layer: crate::PanelLayer::Bottom,
keyboard: crate::PanelKeyboard::OnDemand,
..desc
};
shell.set_panel(widget, size, &lowered).expect("set_panel");
shell.pump(&mut |_| {});
let (_, ex_style, _) = styles_and_rect(hwnd);
assert_eq!(ex_style & style::EX_TOPMOST, 0, "left the topmost band");
assert_eq!(ex_style & style::EX_NO_ACTIVATE, 0, "can take the keyboard");
assert_ne!(ex_style & style::EX_TOOL_WINDOW, 0, "still a panel");
shell.set_panel(widget, size, &desc).expect("set_panel");
shell.pump(&mut |_| {});
let (_, ex_style, _) = styles_and_rect(hwnd);
assert_ne!(ex_style & style::EX_TOPMOST, 0, "back in the topmost band");
assert_ne!(ex_style & style::EX_NO_ACTIVATE, 0);
shell.destroy_window(widget).expect("destroy");
}
const FULL_SCREEN_PATIENCE: Duration = Duration::from_secs(10);
const QUNS_BUSY: i32 = 2;
const QUNS_RUNNING_D3D_FULL_SCREEN: i32 = 3;
const QUNS_QUIET_TIME: i32 = 6;
fn notification_state() -> Option<i32> {
#[link(name = "shell32")]
unsafe extern "system" {
fn SHQueryUserNotificationState(state: *mut i32) -> i32;
}
let mut state = 0;
(unsafe { SHQueryUserNotificationState(&raw mut state) } == 0).then_some(state)
}
fn wait_for_no_full_screen_app() {
let deadline = Instant::now() + FULL_SCREEN_PATIENCE;
let mut state = notification_state();
while matches!(state, Some(QUNS_BUSY | QUNS_RUNNING_D3D_FULL_SCREEN)) {
assert!(
Instant::now() < deadline,
"Explorer still believes a full-screen application is up (state {state:?})"
);
std::thread::sleep(Duration::from_millis(20));
state = notification_state();
}
}
fn wait_for_full_screen_app(shell: &mut Win32Shell, bar: WindowId, active: bool) {
const PATIENCE: Duration = FULL_SCREEN_PATIENCE;
let deadline = Instant::now() + PATIENCE;
let mut seen = Vec::new();
while Instant::now() < deadline {
let mut found = false;
shell.pump(&mut |event| {
if let ShellEvent::FullScreenApp {
window,
active: now,
} = event
{
seen.push((window, now));
found |= window == bar && now == active;
}
});
if found {
return;
}
std::thread::sleep(Duration::from_millis(20));
}
panic!(
"no FullScreenApp {{ active: {active} }} for the bar; saw {seen:?}; Explorer's \
notification state is {:?}",
notification_state()
);
}
#[test]
#[ignore = "reserves part of the real desktop's work area: run-win32-e2e.ps1 runs it"]
fn a_reserving_bar_is_told_when_a_full_screen_window_opens_and_closes() {
let mut shell = shell();
let primary = shell
.monitors()
.iter()
.find(|monitor| monitor.is_primary)
.cloned()
.expect("a primary monitor");
let before = work_area_until(&mut shell, |_| true);
let bar = shell
.create_window(&panel_desc(
"crcbl full-screen appbar",
LogicalSize::new(0.0, 30.0),
crate::PanelDesc {
monitor: Some(primary.id),
anchor: crate::PanelAnchor::TOP
| crate::PanelAnchor::LEFT
| crate::PanelAnchor::RIGHT,
reserve: crate::Reserve::OwnExtent,
..crate::PanelDesc::default()
},
))
.expect("a reserving panel");
work_area_until(&mut shell, |area| area.y != before.y);
wait_for_no_full_screen_app();
let game = shell
.create_window(&WindowDesc {
title: "crcbl full-screen game",
mode: DisplayMode::Borderless {
monitor: Some(primary.id),
},
..WindowDesc::default()
})
.expect("a borderless window over the monitor");
let game_hwnd = hwnd_of(&shell, game);
focus_and_confirm(&mut shell, game, game_hwnd);
if notification_state() == Some(QUNS_QUIET_TIME) {
eprintln!(
"Explorer is in its quiet period and detects no full-screen window; \
delivering ABN_FULLSCREENAPP to the bar directly"
);
let bar_hwnd = hwnd_of(&shell, bar);
for (open, l_param) in [(true, 1), (false, 0)] {
unsafe {
ffi::SendMessageW(
bar_hwnd,
msg::APP_BAR,
ffi::appbar::FULL_SCREEN_APP as usize,
l_param,
);
}
wait_for_full_screen_app(&mut shell, bar, open);
}
shell.destroy_window(game).expect("destroy the game");
} else {
wait_for_full_screen_app(&mut shell, bar, true);
shell.destroy_window(game).expect("destroy the game");
wait_for_full_screen_app(&mut shell, bar, false);
}
shell.destroy_window(bar).expect("destroy the bar");
assert_eq!(
work_area_until(&mut shell, |area| area == before),
before,
"the edge is given back"
);
}
fn has_input_context(hwnd: Handle) -> bool {
unsafe {
let context = ffi::ImmGetContext(hwnd);
if !context.is_null() {
ffi::ImmReleaseContext(hwnd, context);
}
!context.is_null()
}
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_window_has_no_input_context_until_a_field_asks_for_one() {
let mut shell = shell();
let window = shell
.create_window(&WindowDesc::default())
.expect("a window");
let hwnd = hwnd_of(&shell, window);
assert!(!has_input_context(hwnd), "off as it is created");
shell
.set_text_input(window, Some(TextPurpose::Normal))
.expect("turn the input method on");
assert!(has_input_context(hwnd), "the default context is back");
shell
.set_text_input(window, Some(TextPurpose::Password))
.expect("a password field");
assert!(
!has_input_context(hwnd),
"a password keeps the input method out"
);
shell
.set_text_input(window, Some(TextPurpose::Normal))
.expect("an ordinary field again");
assert!(
has_input_context(hwnd),
"and an ordinary field lets it back"
);
shell
.set_text_input(window, None)
.expect("turn the input method off");
assert!(!has_input_context(hwnd), "and gone again");
shell.destroy_window(window).expect("destroy");
}
fn popup_desc(title: &str, size: LogicalSize, popup: crate::PopupDesc) -> WindowDesc<'_> {
WindowDesc {
title,
size,
role: crate::WindowRole::Popup(popup),
..WindowDesc::default()
}
}
fn menu_against(parent: WindowId, input: crate::PopupInput) -> crate::PopupDesc {
crate::PopupDesc {
parent,
anchor: crate::LogicalRect::new(
crate::LogicalPoint::new(10.0, 10.0),
LogicalSize::new(100.0, 20.0),
),
placement: crate::PopupPlacement::Below,
input,
}
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_menu_opens_below_its_button_and_goes_with_its_window() {
use super::super::ffi::style;
let mut shell = shell();
let parent = window(&mut shell);
shell.pump(&mut |_| {});
let parent_hwnd = hwnd_of(&shell, parent);
let scale = shell
.window_state(parent)
.expect("live")
.scale_factor()
.expect("configured");
let menu = shell
.create_window(&popup_desc(
"crcbl popup menu",
LogicalSize::new(200.0, 150.0),
menu_against(parent, crate::PopupInput::Pointer),
))
.expect("a popup");
shell.pump(&mut |_| {});
let mut origin = Point::default();
unsafe { ffi::ClientToScreen(parent_hwnd, &raw mut origin) };
let px = |logical: f64| (logical * scale).round() as i32;
let (_, ex_style, rect) = styles_and_rect(hwnd_of(&shell, menu));
assert_eq!(
(rect.left, rect.top),
(origin.x + px(10.0), origin.y + px(30.0)),
"below the button, left edges aligned"
);
assert_eq!(
shell.window_state(menu).expect("live").size(),
Some(LogicalSize::new(200.0, 150.0).to_physical(scale))
);
assert_ne!(
ex_style & style::EX_NO_ACTIVATE,
0,
"a pointer popup takes no keyboard"
);
let mut destroyed = Vec::new();
shell.pump(&mut |_| {});
shell.destroy_window(parent).expect("destroy");
shell.pump(&mut |event| {
if let ShellEvent::WindowDestroyed { window } = event {
destroyed.push(window);
}
});
assert_eq!(
destroyed,
[menu, parent],
"the popup first, then its window"
);
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_repositioned_menu_follows_its_new_anchor_at_its_new_size() {
let mut shell = shell();
let parent = window(&mut shell);
shell.pump(&mut |_| {});
let parent_hwnd = hwnd_of(&shell, parent);
let scale = shell
.window_state(parent)
.expect("live")
.scale_factor()
.expect("configured");
let menu = shell
.create_window(&popup_desc(
"crcbl repositioned menu",
LogicalSize::new(200.0, 150.0),
menu_against(parent, crate::PopupInput::Pointer),
))
.expect("a popup");
shell.pump(&mut |_| {});
let moved_to = crate::LogicalRect::new(
crate::LogicalPoint::new(60.0, 10.0),
LogicalSize::new(100.0, 20.0),
);
shell
.reposition_popup(menu, moved_to, LogicalSize::new(240.0, 90.0))
.expect("reposition");
shell.pump(&mut |_| {});
let mut origin = Point::default();
unsafe { ffi::ClientToScreen(parent_hwnd, &raw mut origin) };
let px = |logical: f64| (logical * scale).round() as i32;
let (_, _, rect) = styles_and_rect(hwnd_of(&shell, menu));
assert_eq!(
(rect.left, rect.top),
(origin.x + px(60.0), origin.y + px(30.0)),
"below the new anchor, left edges aligned"
);
assert_eq!(
shell.window_state(menu).expect("live").size(),
Some(LogicalSize::new(240.0, 90.0).to_physical(scale)),
"the new size arrived as a resize"
);
assert!(
matches!(
shell.reposition_popup(parent, moved_to, LogicalSize::new(1.0, 1.0)),
Err(ShellError::InvalidDescriptor(_))
),
"a toplevel is not a popup"
);
shell.destroy_window(parent).expect("destroy");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_grabbing_popup_closes_when_it_loses_the_keyboard_and_a_pointer_one_does_not() {
let mut shell = shell();
let parent = window(&mut shell);
shell.pump(&mut |_| {});
let grab = shell
.create_window(&popup_desc(
"crcbl popup grab",
LogicalSize::new(200.0, 150.0),
menu_against(parent, crate::PopupInput::Grab),
))
.expect("a popup");
let pointer = shell
.create_window(&popup_desc(
"crcbl popup pointer",
LogicalSize::new(200.0, 150.0),
menu_against(parent, crate::PopupInput::Pointer),
))
.expect("a popup");
shell.pump(&mut |_| {});
let grab_hwnd = hwnd_of(&shell, grab);
focus_and_confirm(&mut shell, grab, grab_hwnd);
send_focus(grab_hwnd, false);
send_focus(hwnd_of(&shell, pointer), false);
let mut asked = Vec::new();
shell.pump(&mut |event| {
if let ShellEvent::CloseRequested { window } = event {
asked.push(window);
}
});
assert_eq!(asked, [grab], "only the grabbing popup is dismissed");
shell.destroy_window(parent).expect("destroy");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn escape_on_a_grabbing_popup_is_delivered_then_asks_it_to_close() {
const VK_ESCAPE: usize = 0x1B;
let mut shell = shell();
let parent = window(&mut shell);
shell.pump(&mut |_| {});
let grab = shell
.create_window(&popup_desc(
"crcbl popup escape grab",
LogicalSize::new(200.0, 150.0),
menu_against(parent, crate::PopupInput::Grab),
))
.expect("a popup");
let pointer = shell
.create_window(&popup_desc(
"crcbl popup escape pointer",
LogicalSize::new(200.0, 150.0),
menu_against(parent, crate::PopupInput::Pointer),
))
.expect("a popup");
shell.pump(&mut |_| {});
if shell.window_state(grab).expect("live").close_pending {
shell
.reply_close_request(grab, crate::CloseReply::Keep)
.expect("keep");
}
let (grab_hwnd, pointer_hwnd) = (hwnd_of(&shell, grab), hwnd_of(&shell, pointer));
let pump_edges = |shell: &mut Win32Shell| {
let mut edges = Vec::new();
shell.pump(&mut |event| match event {
ShellEvent::Key {
window,
key_code: Some(KeyCode::Escape),
state,
repeat,
..
} => edges.push((window, Some((state, repeat)))),
ShellEvent::CloseRequested { window } => edges.push((window, None)),
_ => {}
});
edges
};
send_key(grab_hwnd, msg::KEY_DOWN, VK_ESCAPE, 0x0001, false, false);
assert_eq!(
pump_edges(&mut shell),
[(grab, Some((ButtonState::Pressed, false))), (grab, None)],
"the key, then the request"
);
shell
.reply_close_request(grab, crate::CloseReply::Keep)
.expect("keep");
send_key(grab_hwnd, msg::KEY_DOWN, VK_ESCAPE, 0x0001, false, true);
send_key(grab_hwnd, msg::KEY_UP, VK_ESCAPE, 0x0001, false, true);
assert!(
pump_edges(&mut shell).iter().all(|(_, key)| key.is_some()),
"neither a repeat nor a release dismisses"
);
send_key(grab_hwnd, msg::KEY_DOWN, VK_ESCAPE, 0x0001, false, false);
assert_eq!(pump_edges(&mut shell).last(), Some(&(grab, None)));
send_key(pointer_hwnd, msg::KEY_DOWN, VK_ESCAPE, 0x0001, false, false);
assert_eq!(
pump_edges(&mut shell),
[(pointer, Some((ButtonState::Pressed, false)))]
);
shell.destroy_window(parent).expect("destroy");
}
#[test]
#[ignore = "opens windows on the real desktop: run-win32-e2e.ps1 runs it"]
fn a_passthrough_popup_is_not_there_for_the_pointer() {
let mut shell = shell();
let parent = window(&mut shell);
shell.pump(&mut |_| {});
let hit = |shell: &mut Win32Shell, input| {
let popup = shell
.create_window(&popup_desc(
"crcbl popup hit test",
LogicalSize::new(200.0, 150.0),
menu_against(parent, input),
))
.expect("a popup");
shell.pump(&mut |_| {});
let hwnd = hwnd_of(shell, popup);
let (_, _, rect) = styles_and_rect(hwnd);
let inside = ((rect.top + 5) << 16) as isize | (rect.left + 5) as isize;
let answer = unsafe { ffi::SendMessageW(hwnd, msg::NC_HIT_TEST, 0, inside) };
shell.destroy_window(popup).expect("destroy");
answer
};
assert_eq!(
hit(&mut shell, crate::PopupInput::Passthrough),
value::HT_TRANSPARENT
);
assert_ne!(
hit(&mut shell, crate::PopupInput::Pointer),
value::HT_TRANSPARENT
);
shell.destroy_window(parent).expect("destroy");
}