Compare commits

..

15 Commits

Author SHA1 Message Date
Kirill Chibisov
e9809ef54b Winit version 0.30.13 2026-03-02 22:49:04 +09:00
Kirill Chibisov
efb5b37fff chore: fix ci 2026-03-02 22:49:04 +09:00
Kirill Chibisov
a9baf5ecda fix(android): Populate KeyEvent.text via Key::to_text()
The `text` field on `KeyEvent` was hardcoded to `None` on Android,
making it impossible for custom `NativeActivity` subclasses that
show the IME to receive functional text input using *for example* the
existing `winit-egui` crate which relies on this field being set.

Use `Key::to_text()` on press events to derive `text` from
`logical_key`, matching the convention used by the Windows and macOS
backends.

Supposedly that doesn't include all kinds of special virtual unicode
keys, but at least the basics work this way.
2026-03-02 22:49:04 +09:00
Pedro Macedo
6bb43fd130 wayland: implement resize increments 2026-03-02 22:49:04 +09:00
Kotomine Shiki
17a73f4dd4 win32: fix ime setcontext lparam
Fixes #3893.
2026-03-02 22:49:04 +09:00
Takaranoao
bccc568345 fix(macOS): clamp IME selected_range to prevent substringToIndex crash
macOS native Pinyin IME can send a selected_range that exceeds the
marked text string length (e.g. index 8 for a 6-character string).
This caused an NSRangeException in substringToIndex:, crashing the
application with SIGABRT.

Clamp both location and end to the string's UTF-16 length before
calling substringToIndex.
2026-03-02 22:49:04 +09:00
SuchAFuriousDeath
69b8a07ae0 winit-x11: fix debug mode overflow panic in set_timestamp
Fixes #4484
2026-03-02 22:49:04 +09:00
Silico_Biomancer
3eb731f8b5 winit-x11: replace xfixes with x11rb in set_hittest
The xfixes implementation is not that reliable and rather simple to
replace, so use x11rb to implement the same functionality.

Fixes #4120.
Co-authored-by: avitran0 <holyhades64@gmail.com>
2026-03-02 22:49:04 +09:00
itsamine27
7035dd554f winit-win32: Fix ABI mismatch in INIT_MAIN_THREAD_ID
Fixes #4435.
2026-03-02 22:49:04 +09:00
Dan Harris
ab4c6bfc82 macOS: fix a crash when dragging non-file content onto window
Winit only supports text, thus we should ignore the rest
instead of crashing.
2026-03-02 22:49:04 +09:00
Kirill Chibisov
f6893a4390 Winit version 0.30.12 2025-07-27 21:59:42 +09:00
Kirill Chibisov
c0a8bedee2 chore: fix typos from updated typos tool 2025-07-27 21:59:42 +09:00
Kirill Chibisov
b248ecba31 winit: silence wasm on nightly
The lint is needed for stable, but is no longer present on nightly, so
silence it for the time being.
2025-07-27 21:59:42 +09:00
Kirill Chibisov
b49d34ebf0 ci/deny: add rustix
Will take a while to move to 1.0 for everyone.
2025-07-27 21:59:42 +09:00
Robert Wallis
cc43ea13d9 macOS: fix runtime crash on macos26 "type code 'q', but found 'Q'"
Fixes #4299.
2025-07-27 21:59:42 +09:00
23 changed files with 172 additions and 54 deletions

View File

@@ -111,8 +111,13 @@ jobs:
cargo generate-lockfile
cargo update -p ahash --precise 0.8.7
cargo update -p bumpalo --precise 3.14.0
cargo update -p softbuffer --precise 0.4.0
cargo update -p objc2-encode --precise 4.0.3
cargo update -p orbclient --precise 0.3.47
cargo update -p image --precise 0.25.0
cargo update -p gethostname@1.1.0 --precise 1.0.2
cargo update -p unicode-ident --precise 1.0.10
cargo update -p syn --precise 2.0.114
- name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.30.11"
version = "0.30.13"
authors = [
"The winit contributors",
"Pierre Krieger <pierre.krieger1708@gmail.com>",
@@ -119,7 +119,7 @@ ndk = { version = "0.9.0", default-features = false }
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
block2 = "0.5.1"
core-foundation = "0.9.3"
objc2 = "0.5.2"
objc2 = { version = "0.5.2", features = ["relax-sign-encoding"] }
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.23.1"

View File

@@ -8,7 +8,7 @@
```toml
[dependencies]
winit = "0.30.11"
winit = "0.30.13"
```
## [Documentation](https://docs.rs/winit)

View File

@@ -40,8 +40,10 @@ multiple-versions = "deny"
skip = [
{ crate = "raw-window-handle", reason = "we depend on multiple behind features" },
{ crate = "bitflags@1", reason = "the ecosystem is in the process of migrating" },
{ crate = "rustix@0.38", reason = "the ecosystem is in the process of migrating" },
{ crate = "linux-raw-sys@0.4", reason = "the ecosystem is in the process of migrating" },
]
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
[bans.build]
include-archives = true
@@ -54,7 +56,7 @@ allow = [
crate = "android-activity"
[[bans.build.bypass]]
allow-globs = ["ci/*", "githooks/*"]
allow-globs = ["ci/*", "githooks/*", "cargo.sh"]
crate = "zerocopy"
[[bans.build.bypass]]

View File

@@ -250,7 +250,7 @@
- On Web, fix some `WindowBuilder` methods doing nothing.
- On Web, fix some `Window` methods using incorrect HTML attributes instead of CSS properties.
- On Web, fix the bfcache by not using the `beforeunload` event and map bfcache loading/unloading to `Suspended`/`Resumed` events.
- On Web, fix touch input not gaining or loosing focus.
- On Web, fix touch input not gaining or losing focus.
- On Web, fix touch location to be as accurate as mouse position.
- On Web, handle coalesced pointer events, which increases the resolution of pointer inputs.
- On Web, implement `Window::focus_window()`.

View File

@@ -1,3 +1,24 @@
## 0.30.13
### Added
- On Wayland, add `Window::set_resize_increments`.
### Fixed
- On macOS, fixed crash when dragging non-file content onto window.
- On X11, fix `set_hittest` not working on some window managers.
- On X11, fix debug mode overflow panic in `set_timestamp`.
- On macOS, fix crash in `set_marked_text` when native Pinyin IME sends out-of-bounds `selected_range`.
- On Windows, fix `WM_IME_SETCONTEXT` IME UI flag masking on `lParam`.
- On Android, populate `KeyEvent::text` and `KeyEvent::text_with_all_modifiers` via `Key::to_text()`.
## 0.30.12
### Fixed
- On macOS, fix crash on macOS 26 by using objc2's `relax-sign-encoding` feature.
## 0.30.11
### Fixed

View File

@@ -621,7 +621,7 @@ pub struct KeyEvent {
///
/// # Example
///
/// In games, you often want to ignore repated key events - this can be
/// In games, you often want to ignore repeated key events - this can be
/// done by ignoring events where this property is set.
///
/// ```

View File

@@ -182,11 +182,11 @@
#![cfg_attr(clippy, deny(warnings))]
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly
// doc
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
#![cfg_attr(docsrs, feature(doc_cfg), doc(auto_cfg(hide(doc, docsrs))))]
#![allow(clippy::missing_safety_doc)]
#![warn(clippy::uninlined_format_args)]
// TODO: wasm-binding needs to be updated for that to be resolved, for now just silence it.
#![cfg_attr(web_platform, allow(unknown_lints, wasm_c_abi))]
#![cfg_attr(web_platform, allow(unknown_lints, renamed_and_removed_lints, wasm_c_abi))]
#[cfg(feature = "rwh_04")]
pub use rwh_04 as raw_window_handle_04;

View File

@@ -62,7 +62,7 @@
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building
//! with `cargo apk`, then the minimal changes would be:
//! 1. Remove `ndk-glue` from your `Cargo.toml`
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.11",
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.13",
//! features = [ "android-native-activity" ] }`
//! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc
//! macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize

View File

@@ -446,6 +446,13 @@ impl<T: 'static> EventLoop<T> {
&mut self.combining_accent,
);
let logical_key = keycodes::to_logical(key_char, keycode);
let text = if state == event::ElementState::Pressed {
logical_key.to_text().map(smol_str::SmolStr::new)
} else {
None
};
let event = event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::KeyboardInput {
@@ -453,10 +460,10 @@ impl<T: 'static> EventLoop<T> {
event: event::KeyEvent {
state,
physical_key: keycodes::to_physical_key(keycode),
logical_key: keycodes::to_logical(key_char, keycode),
logical_key,
location: keycodes::to_location(keycode),
repeat: key.repeat_count() > 0,
text: None,
text,
platform_specific: KeyEventExtra {},
},
is_synthetic: false,

View File

@@ -99,7 +99,7 @@ impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
WlKeyboardEvent::Leave { surface, .. } => {
let window_id = wayland::make_wid(&surface);
// NOTE: we should drop the repeat regardless whethere it was for the present
// NOTE: we should drop the repeat regardless whether it was for the present
// window of for the window which just went gone.
keyboard_state.current_repeat = None;
if let Some(token) = keyboard_state.repeat_token.take() {

View File

@@ -166,6 +166,12 @@ impl Window {
Cursor::Custom(cursor) => window_state.set_custom_cursor(cursor),
}
// Apply resize increments.
if let Some(increments) = attributes.resize_increments {
let increments = increments.to_logical(window_state.scale_factor());
window_state.set_resize_increments(Some(increments));
}
// Activate the window when the token is passed.
if let (Some(xdg_activation), Some(token)) =
(xdg_activation.as_ref(), attributes.platform_specific.activation_token)
@@ -333,12 +339,19 @@ impl Window {
#[inline]
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
None
let window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor();
window_state
.resize_increments()
.map(|size| super::logical_to_physical_rounded(size, scale_factor))
}
#[inline]
pub fn set_resize_increments(&self, _increments: Option<Size>) {
warn!("`set_resize_increments` is not implemented for Wayland");
pub fn set_resize_increments(&self, increments: Option<Size>) {
let mut window_state = self.window_state.lock().unwrap();
let scale_factor = window_state.scale_factor();
let increments = increments.map(|size| size.to_logical(scale_factor));
window_state.set_resize_increments(increments);
}
#[inline]

View File

@@ -127,6 +127,7 @@ pub struct WindowState {
/// Min size.
min_inner_size: LogicalSize<u32>,
max_inner_size: Option<LogicalSize<u32>>,
resize_increments: Option<LogicalSize<u32>>,
/// The size of the window when no states were applied to it. The primary use for it
/// is to fallback to original window size, before it was maximized, if the compositor
@@ -202,6 +203,7 @@ impl WindowState {
last_configure: None,
max_inner_size: None,
min_inner_size: MIN_WINDOW_SIZE,
resize_increments: None,
pointer_constraints,
pointers: Default::default(),
queue_handle: queue_handle.clone(),
@@ -340,6 +342,42 @@ impl WindowState {
.unwrap_or(new_size.height);
}
// Apply size increments.
//
// We conditionally apply increments to avoid conflicts with the compositor's layout rules:
// 1. If the window is floating (constrain == true), we snap to increments to ensure the
// app's grid alignment.
// 2. If the user is interactively resizing (is_resizing), we snap the size to provide
// feedback.
//
// However, we MUST NOT snap if the compositor enforces a specific size (constrain == false,
// or states like Maximized/Tiled). Snapping in these cases (e.g. corner tiling) would
// shrink the window below the allocated area, creating visible gaps between valid
// windows or screen edges.
if (constrain || configure.is_resizing())
&& !configure.is_maximized()
&& !configure.is_fullscreen()
&& !configure.is_tiled()
{
if let Some(increments) = self.resize_increments {
// We use min size as a base size for the increments, similar to how X11 does it.
//
// This ensures that we can always reach the min size and the increments are
// calculated from it.
let (delta_width, delta_height) = (
new_size.width.saturating_sub(self.min_inner_size.width),
new_size.height.saturating_sub(self.min_inner_size.height),
);
let width =
self.min_inner_size.width + (delta_width / increments.width) * increments.width;
let height = self.min_inner_size.height
+ (delta_height / increments.height) * increments.height;
new_size = (width, height).into();
}
}
let new_state = configure.state;
let old_state = self.last_configure.as_ref().map(|configure| configure.state);
@@ -725,6 +763,18 @@ impl WindowState {
self.selected_cursor = SelectedCursor::Custom(cursor);
}
/// Set the resize increments of the window.
pub fn set_resize_increments(&mut self, increments: Option<LogicalSize<u32>>) {
self.resize_increments = increments;
// NOTE: We don't update the window size here, because it will be done on the next resize
// or configure event.
}
/// Get the resize increments of the window.
pub fn resize_increments(&self) -> Option<LogicalSize<u32>> {
self.resize_increments
}
fn apply_custom_cursor(&self, cursor: &CustomCursor) {
self.apply_on_pointer(|pointer, data| {
let surface = pointer.surface();

View File

@@ -81,7 +81,7 @@ extern "C" fn preedit_draw_callback(
call_data.chg_first as usize..(call_data.chg_first + call_data.chg_length) as usize;
if chg_range.start > client_data.text.len() || chg_range.end > client_data.text.len() {
tracing::warn!(
"invalid chg range: buffer length={}, but chg_first={} chg_lengthg={}",
"invalid chg range: buffer length={}, but chg_first={} chg_length={}",
client_data.text.len(),
call_data.chg_first,
call_data.chg_length
@@ -158,7 +158,7 @@ impl PreeditCallbacks {
pub fn new(client_data: ffi::XPointer) -> PreeditCallbacks {
let start_callback = create_xim_callback(client_data, unsafe {
mem::transmute::<usize, unsafe extern "C" fn(ffi::XIM, ffi::XPointer, ffi::XPointer)>(
preedit_start_callback as usize,
preedit_start_callback as *const () as usize,
)
});
let done_callback = create_xim_callback(client_data, preedit_done_callback);

View File

@@ -51,10 +51,9 @@ impl ImeInner {
}
pub unsafe fn close_im_if_necessary(&self) -> Result<bool, XError> {
if !self.is_destroyed && self.im.is_some() {
unsafe { close_im(&self.xconn, self.im.as_ref().unwrap().im) }.map(|_| true)
} else {
Ok(false)
match self.im.as_ref() {
Some(im) if !self.is_destroyed => unsafe { close_im(&self.xconn, im.im).map(|_| true) },
_ => Ok(false),
}
}

View File

@@ -8,9 +8,8 @@ use std::{cmp, env};
use tracing::{debug, info, warn};
use x11rb::connection::Connection;
use x11rb::properties::{WmHints, WmSizeHints, WmSizeHintsSpecification};
use x11rb::protocol::shape::SK;
use x11rb::protocol::xfixes::{ConnectionExt, RegionWrapper};
use x11rb::protocol::xproto::{self, ConnectionExt as _, Rectangle};
use x11rb::protocol::shape::{ConnectionExt as ShapeExt, SK, SO};
use x11rb::protocol::xproto::{self, ClipOrdering, ConnectionExt as _, Rectangle};
use x11rb::protocol::{randr, xinput};
use crate::cursor::{Cursor, CustomCursor as RootCustomCursor};
@@ -1622,6 +1621,15 @@ impl UnownedWindow {
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
// Implement cursor hittest for X11 by either setting an empty or full window input shape.
// In X11, every window has two "shapes":
// * Bounding shape: defines the visible outline of the window.
// * Input shape: defines the region of the window that receives pointer/keyboard events.
// If the input shape is the full window rectangle, the window behaves normally.
// If the input shape is empty, the window is completely clickthrough.
// Here, we implement hit test by mapping `hittest = true` to "restore a full input shape"
// and `hittest = false` to "clear the input shape" (empty list of rectangles).
let mut rectangles: Vec<Rectangle> = Vec::new();
if hittest {
let size = self.inner_size();
@@ -1632,11 +1640,18 @@ impl UnownedWindow {
height: size.height as u16,
})
}
let region = RegionWrapper::create_region(self.xconn.xcb_connection(), &rectangles)
.map_err(|_e| ExternalError::Ignored)?;
self.xconn
.xcb_connection()
.xfixes_set_window_shape_region(self.xwindow, SK::INPUT, 0, 0, region.region())
.shape_rectangles(
SO::SET,
SK::INPUT,
ClipOrdering::UNSORTED,
self.xwindow,
0,
0,
&rectangles,
)
.map_err(|_e| ExternalError::Ignored)?;
self.shared_state_lock().cursor_hittest = Some(hittest);
Ok(())

View File

@@ -234,9 +234,7 @@ impl XConnection {
// Store the timestamp in the slot if it's greater than the last one.
let mut last_timestamp = self.timestamp.load(Ordering::Relaxed);
loop {
let wrapping_sub = |a: xproto::Timestamp, b: xproto::Timestamp| (a as i32) - (b as i32);
if wrapping_sub(timestamp, last_timestamp) <= 0 {
if (timestamp as i32).wrapping_sub(last_timestamp as i32) <= 0 {
break;
}

View File

@@ -128,7 +128,7 @@ pub(crate) fn create_key_event(ns_event: &NSEvent, is_press: bool, is_repeat: bo
let logical_key = match text_with_all_modifiers.as_ref() {
// Only checking for ctrl and cmd here, not checking for alt because we DO want to
// include its effect in the key. For example if -on the Germay layout- one
// include its effect in the key. For example if -on the German layout- one
// presses alt+8, the logical key should be "{"
// Also not checking if this is a release event because then this issue would
// still affect the key release.

View File

@@ -316,9 +316,15 @@ declare_class!(
// sending a `None` cursor range.
None
} else {
// Clamp to string length to avoid NSRangeException from out-of-bounds
// indices sent by macOS IME (e.g. native Pinyin, see
// https://github.com/alacritty/alacritty/issues/8791).
let len = string.length();
let location = selected_range.location.min(len);
let end = selected_range.end().min(len);
// Convert the selected range from UTF-16 indices to UTF-8 indices.
let sub_string_a = unsafe { string.substringToIndex(selected_range.location) };
let sub_string_b = unsafe { string.substringToIndex(selected_range.end()) };
let sub_string_a = unsafe { string.substringToIndex(location) };
let sub_string_b = unsafe { string.substringToIndex(end) };
let lowerbound_utf8 = sub_string_a.len();
let upperbound_utf8 = sub_string_b.len();
Some((lowerbound_utf8, upperbound_utf8))

View File

@@ -373,7 +373,10 @@ declare_class!(
use std::path::PathBuf;
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
let filenames = match pb.propertyListForType(unsafe { NSFilenamesPboardType }) {
Some(filenames) => filenames,
None => return false.into(),
};
let filenames: Retained<NSArray<NSString>> = unsafe { Retained::cast(filenames) };
filenames.into_iter().for_each(|file| {
@@ -399,7 +402,10 @@ declare_class!(
use std::path::PathBuf;
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
let filenames = match pb.propertyListForType(unsafe { NSFilenamesPboardType }) {
Some(filenames) => filenames,
None => return false.into(),
};
let filenames: Retained<NSArray<NSString>> = unsafe { Retained::cast(filenames) };
filenames.into_iter().for_each(|file| {

View File

@@ -574,9 +574,11 @@ fn main_thread_id() -> u32 {
//
// See: https://doc.rust-lang.org/stable/reference/abi.html#the-link_section-attribute
#[link_section = ".CRT$XCU"]
static INIT_MAIN_THREAD_ID: unsafe fn() = {
unsafe fn initer() {
unsafe { MAIN_THREAD_ID = GetCurrentThreadId() };
static INIT_MAIN_THREAD_ID: unsafe extern "C" fn() = {
unsafe extern "C" fn initer() {
unsafe {
MAIN_THREAD_ID = GetCurrentThreadId();
}
}
initer
};
@@ -738,7 +740,7 @@ fn wait_for_messages_impl(
unsafe {
// Either:
// 1. User wants to wait indefinely if timeout is not set.
// 1. User wants to wait indefinitely if timeout is not set.
// 2. We failed to get and set high resolution timer and we need something instead of it.
let wait_duration_ms = timeout.map(dur2timeout).unwrap_or(INFINITE);
@@ -1610,9 +1612,9 @@ unsafe fn public_window_callback_inner(
},
WM_IME_SETCONTEXT => {
// Hide composing text drawn by IME.
let wparam = wparam & (!ISC_SHOWUICOMPOSITIONWINDOW as usize);
result = ProcResult::DefWindowProc(wparam);
// IME UI visibility flags are in lparam.
let lparam = lparam & !(ISC_SHOWUICOMPOSITIONWINDOW as isize);
result = ProcResult::Value(unsafe { DefWindowProcW(window, msg, wparam, lparam) });
},
// this is necessary for us to maintain minimize/restore state
@@ -2639,7 +2641,7 @@ unsafe fn handle_raw_input(userdata: &ThreadMsgTargetData, data: RAWINPUT) {
}
enum PointerMoveKind {
/// Pointer enterd to the window.
/// Pointer entered to the window.
Enter,
/// Pointer leaved the window client area.
Leave,

View File

@@ -465,7 +465,7 @@ enum PartialText {
enum PartialLogicalKey {
/// Use the text provided by the WM_CHAR messages and report that as a `Character` variant. If
/// the text consists of multiple grapheme clusters (user-precieved characters) that means that
/// the text consists of multiple grapheme clusters (user-perceived characters) that means that
/// dead key could not be combined with the second input, and in that case we should fall back
/// to using what would have without a dead-key input.
TextOr(Key),

View File

@@ -884,7 +884,7 @@ impl Window {
///
/// ## Platform-specific
///
/// - **iOS / Android / Web / Wayland / Orbital:** Always returns [`None`].
/// - **iOS / Android / Web / Orbital:** Always returns [`None`].
#[inline]
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
let _span = tracing::debug_span!("winit::Window::resize_increments",).entered();
@@ -900,7 +900,6 @@ impl Window {
///
/// - **macOS:** Increments are converted to logical size and then macOS rounds them to whole
/// numbers.
/// - **Wayland:** Not implemented.
/// - **iOS / Android / Web / Orbital:** Unsupported.
#[inline]
pub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>) {
@@ -1825,10 +1824,11 @@ pub enum WindowLevel {
/// ## Platform-specific
///
/// - **iOS / Android / Web / Windows / X11 / macOS / Orbital:** Unsupported.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum ImePurpose {
/// No special hints for the IME (default).
#[default]
Normal,
/// The IME is used for password input.
Password,
@@ -1838,12 +1838,6 @@ pub enum ImePurpose {
Terminal,
}
impl Default for ImePurpose {
fn default() -> Self {
Self::Normal
}
}
/// An opaque token used to activate the [`Window`].
///
/// [`Window`]: crate::window::Window