Compare commits

..

6 Commits

Author SHA1 Message Date
Mads Marquart
926fb051de Rename window example to full, to signify that it's a large example 2024-03-01 09:45:56 +01:00
Mads Marquart
21fecf2cb2 Inline example helper functionality
It is more clear what's happening when you don't have to jump to the bottom of the file to look at the implementation.
2024-03-01 09:45:54 +01:00
Mads Marquart
c6dfe620ff Remove Display impl for Action 2024-03-01 09:23:52 +01:00
Mads Marquart
1bef238c2a Remove option_as_alt example state 2024-03-01 09:22:25 +01:00
Mads Marquart
1edf4f1238 Remove a level of indirection in main window example
We already have the `Action` enum that abstracts key and mouse event bindings; on top of that, putting each action handler in its own functions seems excessive.
2024-03-01 09:22:24 +01:00
Mads Marquart
72482048a0 Move monitor functionality out of the main window example 2024-03-01 08:37:25 +01:00
68 changed files with 1585 additions and 2453 deletions

View File

@@ -17,24 +17,6 @@ jobs:
- name: Check Formatting - name: Check Formatting
run: cargo fmt -- --check run: cargo fmt -- --check
typos:
name: Check for typos
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: taiki-e/install-action@v2
with:
tool: typos-cli
- name: run typos
run: typos
- name: Typos info
if: failure()
run: |
echo 'To fix typos, please run `typos -w`'
echo 'To check for a diff, run `typos`'
echo 'You can find typos here: https://crates.io/crates/typos'
tests: tests:
name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }} name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }} runs-on: ${{ matrix.platform.os }}

View File

@@ -11,10 +11,6 @@ Unreleased` header.
# Unreleased # Unreleased
- Deprecate `EventLoop::run` in favor of `EventLoop::run_app`.
- Deprecate `EventLoopExtRunOnDemand::run_on_demand` in favor of `EventLoop::run_app_on_demand`.
- Deprecate `EventLoopExtPumpEvents::pump_events` in favor of `EventLoopExtPumpEvents::pump_app_events`.
- Add `ApplicationHandler<T>` trait which mimics `Event<T>`.
- Move `dpi` types to its own crate, and re-export it from the root crate. - Move `dpi` types to its own crate, and re-export it from the root crate.
- Implement `Sync` for `EventLoopProxy<T: Send>`. - Implement `Sync` for `EventLoopProxy<T: Send>`.
- **Breaking:** Move `Window::new` to `ActiveEventLoop::create_window` and `EventLoop::create_window` (with the latter being deprecated). - **Breaking:** Move `Window::new` to `ActiveEventLoop::create_window` and `EventLoop::create_window` (with the latter being deprecated).
@@ -50,22 +46,6 @@ Unreleased` header.
- **Breaking:** Changed the signature of `EventLoop::with_user_event` to return a builder. - **Breaking:** Changed the signature of `EventLoop::with_user_event` to return a builder.
- **Breaking:** Removed `EventLoopBuilder::with_user_event`, the functionality is now available in `EventLoop::with_user_event`. - **Breaking:** Removed `EventLoopBuilder::with_user_event`, the functionality is now available in `EventLoop::with_user_event`.
- Add `Window::default_attributes` to get default `WindowAttributes`. - Add `Window::default_attributes` to get default `WindowAttributes`.
- `log` has been replaced with `tracing`. The old behavior can be emulated by setting the `log` feature on the `tracing` crate.
# 0.29.14
- On X11/Wayland, fix `text` and `text_with_all_modifiers` not being `None` during compose.
- On Wayland, don't reapply cursor grab when unchanged.
- On X11, fix a bug where some mouse events would be unexpectedly filtered out.
# 0.29.13
- On Web, fix possible crash with `ControlFlow::Wait` and `ControlFlow::WaitUntil`.
# 0.29.12
- On X11, fix use after free during xinput2 handling.
- On X11, filter close to zero values in mouse device events
# 0.29.11 # 0.29.11

View File

@@ -1,6 +0,0 @@
# Code of Conduct
The `rust-windowing` project adheres to the [Rust Code of Conduct]. This
describes the minimum behavior expected from all contributors.
[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "winit" name = "winit"
version = "0.29.14" version = "0.29.11"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"] authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library." description = "Cross-platform window creation library."
keywords = ["windowing"] keywords = ["windowing"]
@@ -64,18 +64,17 @@ cfg_aliases = "0.2.0"
[dependencies] [dependencies]
bitflags = "2" bitflags = "2"
cursor-icon = "1.1.0" cursor-icon = "1.1.0"
log = "0.4"
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true } rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true } rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
serde = { workspace = true, optional = true } serde = { workspace = true, optional = true }
smol_str = "0.2.0" smol_str = "0.2.0"
dpi = { path = "dpi" } dpi = { path = "dpi" }
tracing = { version = "0.1.40", default_features = false }
[dev-dependencies] [dev-dependencies]
image = { version = "0.24.0", default-features = false, features = ["png"] } image = { version = "0.24.0", default-features = false, features = ["png"] }
tracing = { version = "0.1.40", default_features = false, features = ["log"] } simple_logger = { version = "4.2.0", default_features = false }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
winit = { path = ".", features = ["rwh_05"] } winit = { path = ".", features = ["rwh_05"] }
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
@@ -188,7 +187,7 @@ wayland-backend = { version = "0.3.0", default_features = false, features = ["cl
wayland-client = { version = "0.31.1", optional = true } wayland-client = { version = "0.31.1", optional = true }
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true } wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true } wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
x11-dl = { version = "2.19.1", optional = true } x11-dl = { version = "2.18.5", optional = true }
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true } x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
xkbcommon-dl = "0.4.2" xkbcommon-dl = "0.4.2"
@@ -258,7 +257,7 @@ web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
[[example]] [[example]]
doc-scrape-examples = true doc-scrape-examples = true
name = "window" name = "full"
[workspace] [workspace]
resolver = "2" resolver = "2"

View File

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

View File

@@ -1,14 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
Please keep one empty line before and after all headers. (This is required for
`git` to produce a conflict when a release is made while a PR is open and the
PR's changelog entry would go into the wrong section).
And please only add new entries to the top of this list, right below the `#
Unreleased` header.
# Unreleased
- Add `LogicalUnit`, `PhysicalUnit` and `PixelUnit` types and related functions.

View File

@@ -35,8 +35,8 @@
//! //!
//! ### Position and Size types //! ### Position and Size types
//! //!
//! The [`PhysicalPosition`] / [`PhysicalSize`] / [`PhysicalUnit`] types correspond with the actual pixels on the //! The [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the
//! device, and the [`LogicalPosition`] / [`LogicalSize`] / [`LogicalUnit`] types correspond to the physical pixels //! device, and the [`LogicalPosition`] / [`LogicalSize`] types correspond to the physical pixels
//! divided by the scale factor. //! divided by the scale factor.
//! //!
//! The position and size types are generic over their exact pixel type, `P`, to allow the //! The position and size types are generic over their exact pixel type, `P`, to allow the
@@ -128,238 +128,6 @@ pub fn validate_scale_factor(scale_factor: f64) -> bool {
scale_factor.is_sign_positive() && scale_factor.is_normal() scale_factor.is_sign_positive() && scale_factor.is_normal()
} }
/// A logical pixel unit.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalUnit<P>(pub P);
impl<P> LogicalUnit<P> {
/// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a logical unit of `0_f64`.
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
#[inline]
pub const fn new(v: P) -> Self {
LogicalUnit(v)
}
}
impl<P: Pixel> LogicalUnit<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalUnit<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<X> {
assert!(validate_scale_factor(scale_factor));
PhysicalUnit::new(self.0.into() * scale_factor).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalUnit<X> {
LogicalUnit(self.0.cast())
}
}
impl<P: Pixel, X: Pixel> From<X> for LogicalUnit<P> {
fn from(v: X) -> LogicalUnit<P> {
LogicalUnit::new(v.cast())
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u8 {
fn from(v: LogicalUnit<P>) -> u8 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u16 {
fn from(v: LogicalUnit<P>) -> u16 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for u32 {
fn from(v: LogicalUnit<P>) -> u32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i8 {
fn from(v: LogicalUnit<P>) -> i8 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i16 {
fn from(v: LogicalUnit<P>) -> i16 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for i32 {
fn from(v: LogicalUnit<P>) -> i32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for f32 {
fn from(v: LogicalUnit<P>) -> f32 {
v.0.cast()
}
}
impl<P: Pixel> From<LogicalUnit<P>> for f64 {
fn from(v: LogicalUnit<P>) -> f64 {
v.0.cast()
}
}
/// A physical pixel unit.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalUnit<P>(pub P);
impl<P> PhysicalUnit<P> {
/// Represents a minimum physical unit of [`f64::MAX`].
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
/// Represents a physical unit of `0_f64`.
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
/// Represents a maximum physical unit that is equal to [`f64::MAX`].
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
#[inline]
pub const fn new(v: P) -> Self {
PhysicalUnit(v)
}
}
impl<P: Pixel> PhysicalUnit<P> {
#[inline]
pub fn from_logical<T: Into<LogicalUnit<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalUnit<X> {
assert!(validate_scale_factor(scale_factor));
LogicalUnit::new(self.0.into() * scale_factor).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalUnit<X> {
PhysicalUnit(self.0.cast())
}
}
impl<P: Pixel, X: Pixel> From<X> for PhysicalUnit<P> {
fn from(v: X) -> PhysicalUnit<P> {
PhysicalUnit::new(v.cast())
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u8 {
fn from(v: PhysicalUnit<P>) -> u8 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u16 {
fn from(v: PhysicalUnit<P>) -> u16 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for u32 {
fn from(v: PhysicalUnit<P>) -> u32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i8 {
fn from(v: PhysicalUnit<P>) -> i8 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i16 {
fn from(v: PhysicalUnit<P>) -> i16 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for i32 {
fn from(v: PhysicalUnit<P>) -> i32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for f32 {
fn from(v: PhysicalUnit<P>) -> f32 {
v.0.cast()
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for f64 {
fn from(v: PhysicalUnit<P>) -> f64 {
v.0.cast()
}
}
/// A pixel unit that's either physical or logical.
pub enum PixelUnit {
Physical(PhysicalUnit<i32>),
Logical(LogicalUnit<f64>),
}
impl PixelUnit {
/// Represents a minimum logical unit of [`f64::MAX`].
pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN));
/// Represents a logical unit of `0_f64`.
pub const ZERO: PixelUnit = PixelUnit::Logical(LogicalUnit::new(0.0));
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
pub const MAX: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MAX));
pub fn new<S: Into<PixelUnit>>(unit: S) -> PixelUnit {
unit.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalUnit<P> {
match *self {
PixelUnit::Physical(unit) => unit.to_logical(scale_factor),
PixelUnit::Logical(unit) => unit.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<P> {
match *self {
PixelUnit::Physical(unit) => unit.cast(),
PixelUnit::Logical(unit) => unit.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalUnit<P>> for PixelUnit {
#[inline]
fn from(unit: PhysicalUnit<P>) -> PixelUnit {
PixelUnit::Physical(unit.cast())
}
}
impl<P: Pixel> From<LogicalUnit<P>> for PixelUnit {
#[inline]
fn from(unit: LogicalUnit<P>) -> PixelUnit {
PixelUnit::Logical(unit.cast())
}
}
/// A position represented in logical pixels. /// A position represented in logical pixels.
/// ///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the /// The position is stored as floats, so please be careful. Casting floats to integers truncates the
@@ -962,43 +730,6 @@ mod tests {
assert!(!validate_scale_factor(f64::NEG_INFINITY)); assert!(!validate_scale_factor(f64::NEG_INFINITY));
} }
#[test]
fn test_logical_unity() {
let log_unit = LogicalUnit::new(1.0);
assert_eq!(log_unit.to_physical::<u32>(1.0), PhysicalUnit::new(1));
assert_eq!(log_unit.to_physical::<u32>(2.0), PhysicalUnit::new(2));
assert_eq!(log_unit.cast::<u32>(), LogicalUnit::new(1));
assert_eq!(
log_unit,
LogicalUnit::from_physical(PhysicalUnit::new(1.0), 1.0)
);
assert_eq!(
log_unit,
LogicalUnit::from_physical(PhysicalUnit::new(2.0), 2.0)
);
assert_eq!(LogicalUnit::from(2.0), LogicalUnit::new(2.0));
let x: f64 = log_unit.into();
assert_eq!(x, 1.0);
}
#[test]
fn test_physical_unit() {
assert_eq!(
PhysicalUnit::from_logical(LogicalUnit::new(1.0), 1.0),
PhysicalUnit::new(1)
);
assert_eq!(
PhysicalUnit::from_logical(LogicalUnit::new(2.0), 0.5),
PhysicalUnit::new(1)
);
assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0,));
assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0));
let x: f64 = PhysicalUnit::new(1).into();
assert_eq!(x, 1.0);
}
#[test] #[test]
fn test_logical_position() { fn test_logical_position() {
let log_pos = LogicalPosition::new(1.0, 2.0); let log_pos = LogicalPosition::new(1.0, 2.0);

View File

@@ -3,32 +3,32 @@
use std::thread; use std::thread;
#[cfg(not(web_platform))] #[cfg(not(web_platform))]
use std::time; use std::time;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time as time; use web_time as time;
use winit::application::ApplicationHandler; use simple_logger::SimpleLogger;
use winit::event::{ElementState, KeyEvent, StartCause, WindowEvent}; use winit::{
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; event::{ElementState, Event, KeyEvent, WindowEvent},
use winit::keyboard::{Key, NamedKey}; event_loop::{ControlFlow, EventLoop},
use winit::window::{Window, WindowId}; keyboard::{Key, NamedKey},
window::Window,
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
const WAIT_TIME: time::Duration = time::Duration::from_millis(100); #[derive(Debug, Clone, Copy, PartialEq, Eq)]
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
enum Mode { enum Mode {
#[default]
Wait, Wait,
WaitUntil, WaitUntil,
Poll, Poll,
} }
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
fn main() -> Result<(), impl std::error::Error> { fn main() -> Result<(), impl std::error::Error> {
tracing_subscriber::fmt::init(); SimpleLogger::new().init().unwrap();
println!("Press '1' to switch to Wait mode."); println!("Press '1' to switch to Wait mode.");
println!("Press '2' to switch to WaitUntil mode."); println!("Press '2' to switch to WaitUntil mode.");
@@ -38,110 +38,96 @@ fn main() -> Result<(), impl std::error::Error> {
let event_loop = EventLoop::new().unwrap(); let event_loop = EventLoop::new().unwrap();
let mut app = ControlFlowDemo::default(); let mut mode = Mode::Wait;
event_loop.run_app(&mut app) let mut request_redraw = false;
} let mut wait_cancelled = false;
let mut close_requested = false;
#[derive(Default)] let mut window = None;
struct ControlFlowDemo { event_loop.run(move |event, event_loop| {
mode: Mode, use winit::event::StartCause;
request_redraw: bool,
wait_cancelled: bool,
close_requested: bool,
window: Option<Window>,
}
impl ApplicationHandler for ControlFlowDemo {
fn new_events(&mut self, _event_loop: &ActiveEventLoop, cause: StartCause) {
println!("new_events: {cause:?}");
self.wait_cancelled = match cause {
StartCause::WaitCancelled { .. } => self.mode == Mode::WaitUntil,
_ => false,
}
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes().with_title(
"Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
);
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
_event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
println!("{event:?}"); println!("{event:?}");
match event { match event {
WindowEvent::CloseRequested => { Event::NewEvents(start_cause) => {
self.close_requested = true; wait_cancelled = match start_cause {
StartCause::WaitCancelled { .. } => mode == Mode::WaitUntil,
_ => false,
}
} }
WindowEvent::KeyboardInput { Event::Resumed => {
event: let window_attributes = Window::default_attributes().with_title(
KeyEvent { "Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.",
logical_key: key, );
state: ElementState::Pressed, window = Some(event_loop.create_window(window_attributes).unwrap());
.. }
}, Event::WindowEvent { event, .. } => match event {
.. WindowEvent::CloseRequested => {
} => match key.as_ref() { close_requested = true;
// WARNING: Consider using `key_without_modifiers()` if available on your platform.
// See the `key_binding` example
Key::Character("1") => {
self.mode = Mode::Wait;
println!("\nmode: {:?}\n", self.mode);
} }
Key::Character("2") => { WindowEvent::KeyboardInput {
self.mode = Mode::WaitUntil; event:
println!("\nmode: {:?}\n", self.mode); KeyEvent {
} logical_key: key,
Key::Character("3") => { state: ElementState::Pressed,
self.mode = Mode::Poll; ..
println!("\nmode: {:?}\n", self.mode); },
} ..
Key::Character("r") => { } => match key.as_ref() {
self.request_redraw = !self.request_redraw; // WARNING: Consider using `key_without_modifiers()` if available on your platform.
println!("\nrequest_redraw: {}\n", self.request_redraw); // See the `key_binding` example
} Key::Character("1") => {
Key::Named(NamedKey::Escape) => { mode = Mode::Wait;
self.close_requested = true; println!("\nmode: {mode:?}\n");
}
Key::Character("2") => {
mode = Mode::WaitUntil;
println!("\nmode: {mode:?}\n");
}
Key::Character("3") => {
mode = Mode::Poll;
println!("\nmode: {mode:?}\n");
}
Key::Character("r") => {
request_redraw = !request_redraw;
println!("\nrequest_redraw: {request_redraw}\n");
}
Key::Named(NamedKey::Escape) => {
close_requested = true;
}
_ => (),
},
WindowEvent::RedrawRequested => {
let window = window.as_ref().unwrap();
window.pre_present_notify();
fill::fill_window(window);
} }
_ => (), _ => (),
}, },
WindowEvent::RedrawRequested => { Event::AboutToWait => {
let window = self.window.as_ref().unwrap(); if request_redraw && !wait_cancelled && !close_requested {
window.pre_present_notify(); window.as_ref().unwrap().request_redraw();
fill::fill_window(window); }
match mode {
Mode::Wait => event_loop.set_control_flow(ControlFlow::Wait),
Mode::WaitUntil => {
if !wait_cancelled {
event_loop.set_control_flow(ControlFlow::WaitUntil(
time::Instant::now() + WAIT_TIME,
));
}
}
Mode::Poll => {
thread::sleep(POLL_SLEEP_TIME);
event_loop.set_control_flow(ControlFlow::Poll);
}
};
if close_requested {
event_loop.exit();
}
} }
_ => (), _ => (),
} }
} })
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if self.request_redraw && !self.wait_cancelled && !self.close_requested {
self.window.as_ref().unwrap().request_redraw();
}
match self.mode {
Mode::Wait => event_loop.set_control_flow(ControlFlow::Wait),
Mode::WaitUntil => {
if !self.wait_cancelled {
event_loop
.set_control_flow(ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME));
}
}
Mode::Poll => {
thread::sleep(POLL_SLEEP_TIME);
event_loop.set_control_flow(ControlFlow::Poll);
}
};
if self.close_requested {
event_loop.exit();
}
}
} }

847
examples/full.rs Normal file
View File

@@ -0,0 +1,847 @@
//! An example showcasing various functionality that Winit has.
//!
//! Often used by Winit developers to test certain functionality, may be a bit
//! verbose.
use std::collections::HashMap;
use std::error::Error;
#[cfg(not(any(android_platform, ios_platform)))]
use std::num::NonZeroU32;
use cursor_icon::CursorIcon;
#[cfg(not(any(android_platform, ios_platform)))]
use rwh_05::HasRawDisplayHandle;
#[cfg(not(any(android_platform, ios_platform)))]
use softbuffer::{Context, Surface};
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event::{DeviceEvent, DeviceId, ElementState, Event, Ime, KeyEvent, WindowEvent};
use winit::event::{MouseButton, MouseScrollDelta};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{Key, ModifiersState};
use winit::window::{
Cursor, CursorGrabMode, CustomCursor, Fullscreen, Icon, ResizeDirection, Theme,
};
use winit::window::{Window, WindowId};
#[cfg(macos_platform)]
use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS};
#[cfg(any(x11_platform, wayland_platform))]
use winit::platform::startup_notify::{
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
};
fn main() -> Result<(), Box<dyn Error>> {
let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
let _event_loop_proxy = event_loop.create_proxy();
// Wire the user event from another thread.
#[cfg(not(web_platform))]
std::thread::spawn(move || {
// Wake up the `event_loop` once every second and dispatch a custom event
// from a different thread.
println!("Starting to send user event every second");
loop {
let _ = _event_loop_proxy.send_event(UserEvent::WakeUp);
std::thread::sleep(std::time::Duration::from_secs(1));
}
});
let mut app = Application::new(&event_loop);
event_loop.run(move |event, event_loop| match event {
Event::NewEvents(_) => (),
Event::Resumed => {
println!("Resumed the event loop");
// Create initial window.
app.create_window(event_loop, None)
.expect("failed to create initial window");
app.print_help();
}
Event::AboutToWait => {
if app.windows.is_empty() {
println!("No windows left, exiting...");
event_loop.exit();
}
}
Event::WindowEvent { window_id, event } => {
app.handle_window_event(event_loop, window_id, event)
}
Event::DeviceEvent { device_id, event } => {
app.handle_device_event(event_loop, device_id, event)
}
Event::UserEvent(event) => {
println!("User event: {event:?}");
}
Event::Suspended | Event::LoopExiting | Event::MemoryWarning => (),
})?;
Ok(())
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
enum UserEvent {
WakeUp,
}
/// Application state and event handling.
struct Application {
/// Custom cursors assets.
custom_cursors: Vec<CustomCursor>,
/// Application icon.
icon: Icon,
windows: HashMap<WindowId, WindowState>,
/// Drawing context.
///
/// With OpenGL it could be EGLDisplay.
#[cfg(not(any(android_platform, ios_platform)))]
context: Context,
}
impl Application {
fn new<T>(event_loop: &EventLoop<T>) -> Self {
// SAFETY: the context is dropped inside the loop, since the state we're using
// is moved inside the closure.
#[cfg(not(any(android_platform, ios_platform)))]
let context = unsafe { Context::from_raw(event_loop.raw_display_handle()).unwrap() };
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
// since it seems to work well enough in most cases. Be careful about going too high, or
// you'll be bitten by the low-quality downscaling built into the WM.
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/data/icon.png");
// Load icon
let icon = {
let image = image::open(path)
.expect("Failed to open icon path")
.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
Icon::from_rgba(rgba, width, height).expect("Failed to open icon")
};
println!("Loading cursor assets");
let decode_cursor = |bytes| {
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
let samples = img.into_flat_samples();
let (_, w, h) = samples.extents();
let (w, h) = (w as u16, h as u16);
CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap()
};
let custom_cursors = vec![
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))),
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross2.png"))),
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/gradient.png"))),
];
Self {
#[cfg(not(any(android_platform, ios_platform)))]
context,
custom_cursors,
icon,
windows: Default::default(),
}
}
fn create_window(
&mut self,
event_loop: &ActiveEventLoop,
_tab_id: Option<String>,
) -> Result<WindowId, Box<dyn Error>> {
// TODO read-out activation token.
#[allow(unused_mut)]
let mut window_attributes = Window::default_attributes()
.with_title("Winit window")
.with_transparent(true)
.with_window_icon(Some(self.icon.clone()));
#[cfg(any(x11_platform, wayland_platform))]
if let Some(token) = event_loop.read_token_from_env() {
startup_notify::reset_activation_token_env();
println!("Using token {:?} to activate a window", token);
window_attributes = window_attributes.with_activation_token(token);
}
#[cfg(macos_platform)]
if let Some(tab_id) = _tab_id {
window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
}
let window = event_loop.create_window(window_attributes)?;
#[cfg(ios_platform)]
{
use winit::platform::ios::WindowExtIOS;
window.recognize_doubletap_gesture(true);
window.recognize_pinch_gesture(true);
window.recognize_rotation_gesture(true);
}
#[cfg(not(any(android_platform, ios_platform)))]
let surface = {
// SAFETY: the surface is dropped before the `window` which
// provided it with handle, thus it doesn't outlive it.
let mut surface = unsafe { Surface::new(&self.context, &window)? };
let size = window.inner_size();
if let (Some(width), Some(height)) =
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
{
surface
.resize(width, height)
.expect("failed to resize inner buffer");
};
surface
};
let theme = window.theme().unwrap_or(Theme::Dark);
println!("Theme: {theme:?}");
// Allow IME out of the box.
let ime = true;
window.set_ime_allowed(ime);
let state = WindowState {
window,
custom_idx: self.custom_cursors.len() - 1,
cursor_grab: CursorGrabMode::None,
named_idx: 0,
#[cfg(not(any(android_platform, ios_platform)))]
surface,
theme,
ime,
cursor_position: Default::default(),
cursor_hidden: Default::default(),
modifiers: Default::default(),
occluded: Default::default(),
rotated: Default::default(),
zoom: Default::default(),
};
let window_id = state.window.id();
println!("Created new window with id={window_id:?}");
self.windows.insert(window_id, state);
Ok(window_id)
}
fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
let state = self.windows.get_mut(&window_id).unwrap();
let window = &state.window;
println!("Executing action: {action:?}");
match action {
Action::CloseWindow => {
let _ = self.windows.remove(&window_id);
}
Action::CreateNewWindow => {
#[cfg(any(x11_platform, wayland_platform))]
if let Err(err) = window.request_activation_token() {
println!("Failed to get activation token: {err}");
} else {
return;
}
if let Err(err) = self.create_window(event_loop, None) {
eprintln!("Error creating new window: {err}");
}
}
Action::ToggleResizeIncrements => {
let new_increments = match window.resize_increments() {
Some(_) => None,
None => Some(LogicalSize::new(25.0, 25.0)),
};
println!("Had increments: {}", new_increments.is_none());
window.set_resize_increments(new_increments);
}
Action::ToggleCursorVisibility => {
state.cursor_hidden = !state.cursor_hidden;
window.set_cursor_visible(!state.cursor_hidden);
}
Action::ToggleResizable => {
let resizable = window.is_resizable();
window.set_resizable(!resizable);
}
Action::ToggleDecorations => {
let decorated = window.is_decorated();
window.set_decorations(!decorated);
}
Action::ToggleFullscreen => {
let fullscreen = if window.fullscreen().is_some() {
None
} else {
Some(Fullscreen::Borderless(None))
};
window.set_fullscreen(fullscreen);
}
Action::ToggleMaximize => {
let maximized = window.is_maximized();
window.set_maximized(!maximized);
}
Action::ToggleImeInput => {
state.ime = !state.ime;
window.set_ime_allowed(state.ime);
if let Some(position) = state.ime.then_some(state.cursor_position).flatten() {
window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
}
}
Action::Minimize => {
window.set_minimized(true);
}
Action::NextCursor => {
// Cursor list to cycle through.
const CURSORS: &[CursorIcon] = &[
CursorIcon::Default,
CursorIcon::Crosshair,
CursorIcon::Pointer,
CursorIcon::Move,
CursorIcon::Text,
CursorIcon::Wait,
CursorIcon::Help,
CursorIcon::Progress,
CursorIcon::NotAllowed,
CursorIcon::ContextMenu,
CursorIcon::Cell,
CursorIcon::VerticalText,
CursorIcon::Alias,
CursorIcon::Copy,
CursorIcon::NoDrop,
CursorIcon::Grab,
CursorIcon::Grabbing,
CursorIcon::AllScroll,
CursorIcon::ZoomIn,
CursorIcon::ZoomOut,
CursorIcon::EResize,
CursorIcon::NResize,
CursorIcon::NeResize,
CursorIcon::NwResize,
CursorIcon::SResize,
CursorIcon::SeResize,
CursorIcon::SwResize,
CursorIcon::WResize,
CursorIcon::EwResize,
CursorIcon::NsResize,
CursorIcon::NeswResize,
CursorIcon::NwseResize,
CursorIcon::ColResize,
CursorIcon::RowResize,
];
// Pick the next cursor
state.named_idx = (state.named_idx + 1) % CURSORS.len();
println!("Setting cursor to \"{:?}\"", CURSORS[state.named_idx]);
window.set_cursor(Cursor::Icon(CURSORS[state.named_idx]));
}
Action::NextCustomCursor => {
state.custom_idx = (state.custom_idx + 1) % self.custom_cursors.len();
let cursor = Cursor::Custom(self.custom_cursors[state.custom_idx].clone());
window.set_cursor(cursor);
}
Action::CycleCursorGrab => {
state.cursor_grab = match state.cursor_grab {
CursorGrabMode::None => CursorGrabMode::Confined,
CursorGrabMode::Confined => CursorGrabMode::Locked,
CursorGrabMode::Locked => CursorGrabMode::None,
};
println!("Changing cursor grab mode to {:?}", state.cursor_grab);
if let Err(err) = window.set_cursor_grab(state.cursor_grab) {
eprintln!("Error setting cursor grab: {err}");
}
}
Action::DragWindow => {
if let Err(err) = window.drag_window() {
println!("Error starting window drag: {err}");
} else {
println!("Dragging window Window={:?}", window.id());
}
}
Action::DragResizeWindow => {
let position = match state.cursor_position {
Some(position) => position,
None => {
println!("Drag-resize requires cursor to be inside the window");
return;
}
};
// The amount of points around the window.
const BORDER_SIZE: f64 = 20.0;
let win_size = window.inner_size();
let border_size = BORDER_SIZE * window.scale_factor();
let x_direction = if position.x < border_size {
ResizeDirection::West
} else if position.x > (win_size.width as f64 - border_size) {
ResizeDirection::East
} else {
// Use arbitrary direction instead of None for simplicity.
ResizeDirection::SouthEast
};
let y_direction = if position.y < border_size {
ResizeDirection::North
} else if position.y > (win_size.height as f64 - border_size) {
ResizeDirection::South
} else {
// Use arbitrary direction instead of None for simplicity.
ResizeDirection::SouthEast
};
let direction = match (x_direction, y_direction) {
(ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
(ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
(ResizeDirection::West, _) => ResizeDirection::West,
(ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
(ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
(ResizeDirection::East, _) => ResizeDirection::East,
(_, ResizeDirection::South) => ResizeDirection::South,
(_, ResizeDirection::North) => ResizeDirection::North,
_ => return,
};
if let Err(err) = window.drag_resize_window(direction) {
println!("Error starting window drag-resize: {err}");
} else {
println!("Drag-resizing window Window={:?}", window.id());
}
}
Action::ShowWindowMenu => {
if let Some(position) = state.cursor_position {
window.show_window_menu(position);
}
}
Action::PrintHelp => self.print_help(),
#[cfg(macos_platform)]
Action::CycleOptionAsAlt => {
let new = match window.option_as_alt() {
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
OptionAsAlt::Both => OptionAsAlt::None,
};
println!("Setting option as alt {:?}", new);
window.set_option_as_alt(new);
}
#[cfg(macos_platform)]
Action::CreateNewTab => {
let tab_id = window.tabbing_identifier();
if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
eprintln!("Error creating new window: {err}");
}
}
}
}
fn handle_window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let state = match self.windows.get_mut(&window_id) {
Some(state) => state,
None => return,
};
let window = &state.window;
match event {
// Resize the surface to the new size
WindowEvent::Resized(_size) => {
#[cfg(not(any(android_platform, ios_platform)))]
{
let (width, height) =
match (NonZeroU32::new(_size.width), NonZeroU32::new(_size.height)) {
(Some(width), Some(height)) => (width, height),
_ => return,
};
state
.surface
.resize(width, height)
.expect("failed to resize inner buffer");
}
window.request_redraw();
}
WindowEvent::Focused(focused) => {
if focused {
println!("Window={window_id:?} fosused");
} else {
println!("Window={window_id:?} unfosused");
}
}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
println!("Window={window_id:?} changed scale to {scale_factor}");
}
WindowEvent::ThemeChanged(theme) => {
println!("Theme changed to {theme:?}");
state.theme = theme;
window.request_redraw();
}
#[cfg(not(any(android_platform, ios_platform)))]
WindowEvent::RedrawRequested => {
// Draw the window contents.
if state.occluded {
println!("Skipping drawing occluded window={:?}", window_id);
}
const WHITE: u32 = 0xFFFFFFFF;
const DARK_GRAY: u32 = 0xFF181818;
let color = match state.theme {
Theme::Light => WHITE,
Theme::Dark => DARK_GRAY,
};
let mut buffer = state
.surface
.buffer_mut()
.expect("could not retrieve buffer");
buffer.fill(color);
window.pre_present_notify();
buffer.present().expect("failed presenting to window");
}
#[cfg(any(android_platform, ios_platform))]
WindowEvent::RedrawRequested => {
println!("Drawing but without rendering...");
}
// Change window occlusion state.
WindowEvent::Occluded(occluded) => {
state.occluded = occluded;
if !occluded {
window.request_redraw();
}
}
WindowEvent::CloseRequested => {
println!("Closing Window={window_id:?}");
self.windows.remove(&window_id);
}
WindowEvent::ModifiersChanged(modifiers) => {
state.modifiers = modifiers.state();
println!("Modifiers changed to {:?}", state.modifiers);
}
WindowEvent::MouseWheel { delta, .. } => match delta {
MouseScrollDelta::LineDelta(x, y) => {
println!("Mouse wheel Line Delta: ({x},{y})");
}
MouseScrollDelta::PixelDelta(px) => {
println!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y);
}
},
WindowEvent::KeyboardInput {
event:
KeyEvent {
// Dispatch actions only on press.
state: ElementState::Pressed,
logical_key,
..
},
is_synthetic: false,
..
} => {
if let Key::Character(ch) = logical_key.as_ref() {
let mods = state.modifiers;
if let Some(action) = Self::process_key_binding(&ch.to_uppercase(), &mods) {
self.handle_action(event_loop, window_id, action);
}
}
}
WindowEvent::KeyboardInput { .. } => {}
WindowEvent::MouseInput {
button,
state: ElementState::Pressed,
..
} => {
if let Some(action) = Self::process_mouse_binding(button, &state.modifiers) {
self.handle_action(event_loop, window_id, action);
}
}
WindowEvent::MouseInput { .. } => {}
WindowEvent::CursorLeft { .. } => {
println!("Cursor left Window={window_id:?}");
state.cursor_position = None;
}
WindowEvent::CursorMoved { position, .. } => {
println!("Moved cursor to {position:?}");
state.cursor_position = Some(position);
if state.ime {
window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
}
}
WindowEvent::ActivationTokenDone { token: _token, .. } => {
#[cfg(any(x11_platform, wayland_platform))]
{
startup_notify::set_activation_token_env(_token);
if let Err(err) = self.create_window(event_loop, None) {
eprintln!("Error creating new window: {err}");
}
}
}
WindowEvent::Ime(event) => match event {
Ime::Enabled => println!("IME enabled for Window={window_id:?}"),
Ime::Preedit(text, caret_pos) => {
println!("Preedit: {}, with caret at {:?}", text, caret_pos);
}
Ime::Commit(text) => {
println!("Commited: {}", text);
}
Ime::Disabled => println!("IME disabled for Window={window_id:?}"),
},
WindowEvent::PinchGesture { delta, .. } => {
state.zoom += delta;
let zoom = state.zoom;
if delta > 0.0 {
println!("Zoomed in {delta:.5} (now: {zoom:.5})");
} else {
println!("Zoomed out {delta:.5} (now: {zoom:.5})");
}
}
WindowEvent::RotationGesture { delta, .. } => {
state.rotated += delta;
let rotated = state.rotated;
if delta > 0.0 {
println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
} else {
println!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
}
}
WindowEvent::DoubleTapGesture { .. } => {
println!("Smart zoom");
}
WindowEvent::TouchpadPressure { .. }
| WindowEvent::HoveredFileCancelled
| WindowEvent::CursorEntered { .. }
| WindowEvent::AxisMotion { .. }
| WindowEvent::DroppedFile(_)
| WindowEvent::HoveredFile(_)
| WindowEvent::Destroyed
| WindowEvent::Touch(_)
| WindowEvent::Moved(_) => (),
}
}
fn handle_device_event(&mut self, _: &ActiveEventLoop, _: DeviceId, event: DeviceEvent) {
println!("Device event: {event:?}");
}
/// Process the key binding.
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
KEY_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&key, mods)
.then_some(binding.action)
})
}
/// Process mouse binding.
fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
MOUSE_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&button, mods)
.then_some(binding.action)
})
}
fn print_help(&self) {
fn modifiers_to_string(mods: ModifiersState) -> String {
let mut mods_line = String::new();
// Always add + since it's printed as a part of the bindings.
for (modifier, desc) in [
(ModifiersState::SUPER, "Super+"),
(ModifiersState::ALT, "Alt+"),
(ModifiersState::CONTROL, "Ctrl+"),
(ModifiersState::SHIFT, "Shift+"),
] {
if !mods.contains(modifier) {
continue;
}
mods_line.push_str(desc);
}
mods_line
}
println!("Keyboard bindings:");
for binding in KEY_BINDINGS {
println!(
"{}{:<10} - {:?} ({})",
modifiers_to_string(binding.mods),
binding.trigger,
binding.action,
binding.action.help(),
);
}
println!("Mouse bindings:");
for binding in MOUSE_BINDINGS {
let button_name = match binding.trigger {
MouseButton::Left => "LMB",
MouseButton::Right => "RMB",
MouseButton::Middle => "MMB",
MouseButton::Back => "Back",
MouseButton::Forward => "Forward",
MouseButton::Other(_) => "",
};
println!(
"{}{:<10} - {:?} ({})",
modifiers_to_string(binding.mods),
button_name,
binding.action,
binding.action.help(),
);
}
}
}
/// Extra state on a window used in this example.
struct WindowState {
/// The actual Winit window.
window: Window,
/// IME input.
ime: bool,
/// Render surface.
///
/// NOTE: This surface must be dropped before the `Window`.
#[cfg(not(any(android_platform, ios_platform)))]
surface: Surface,
/// The window theme we're drawing with.
theme: Theme,
/// Cursor position over the window.
cursor_position: Option<PhysicalPosition<f64>>,
/// Window modifiers state.
modifiers: ModifiersState,
/// Occlusion state of the window.
occluded: bool,
/// Current cursor grab mode.
cursor_grab: CursorGrabMode,
/// The amount of zoom into window.
zoom: f64,
/// The amount of rotation of the window.
rotated: f32,
// Cursor states.
named_idx: usize,
custom_idx: usize,
cursor_hidden: bool,
}
struct Binding<T: Eq> {
trigger: T,
mods: ModifiersState,
action: Action,
}
impl<T: Eq> Binding<T> {
const fn new(trigger: T, mods: ModifiersState, action: Action) -> Self {
Self {
trigger,
mods,
action,
}
}
fn is_triggered_by(&self, trigger: &T, mods: &ModifiersState) -> bool {
&self.trigger == trigger && &self.mods == mods
}
}
/// Helper enum describing the different kinds of actions this example can do.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Action {
CloseWindow,
ToggleCursorVisibility,
CreateNewWindow,
ToggleResizeIncrements,
ToggleImeInput,
ToggleDecorations,
ToggleResizable,
ToggleFullscreen,
ToggleMaximize,
Minimize,
NextCursor,
NextCustomCursor,
CycleCursorGrab,
PrintHelp,
DragWindow,
DragResizeWindow,
ShowWindowMenu,
#[cfg(macos_platform)]
CycleOptionAsAlt,
#[cfg(macos_platform)]
CreateNewTab,
}
impl Action {
fn help(&self) -> &'static str {
match self {
Action::CloseWindow => "Close window",
Action::ToggleCursorVisibility => "Hide cursor",
Action::CreateNewWindow => "Create new window",
Action::ToggleImeInput => "Toggle IME input",
Action::ToggleDecorations => "Toggle decorations",
Action::ToggleResizable => "Toggle window resizable state",
Action::ToggleFullscreen => "Toggle fullscreen",
Action::ToggleMaximize => "Maximize",
Action::Minimize => "Minimize",
Action::ToggleResizeIncrements => "Use resize increments when resizing window",
Action::NextCursor => "Advance the cursor to the next value",
Action::NextCustomCursor => "Advance custom cursor to the next value",
Action::CycleCursorGrab => "Cycle through cursor grab mode",
Action::PrintHelp => "Print help",
Action::DragWindow => "Start window drag",
Action::DragResizeWindow => "Start window drag-resize",
Action::ShowWindowMenu => "Show window menu",
#[cfg(macos_platform)]
Action::CycleOptionAsAlt => "Cycle option as alt mode",
#[cfg(macos_platform)]
Action::CreateNewTab => "Create new tab",
}
}
}
const KEY_BINDINGS: &[Binding<&'static str>] = &[
Binding::new("Q", ModifiersState::CONTROL, Action::CloseWindow),
Binding::new("H", ModifiersState::CONTROL, Action::PrintHelp),
Binding::new("F", ModifiersState::CONTROL, Action::ToggleFullscreen),
Binding::new("D", ModifiersState::CONTROL, Action::ToggleDecorations),
Binding::new("I", ModifiersState::CONTROL, Action::ToggleImeInput),
Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab),
Binding::new("P", ModifiersState::CONTROL, Action::ToggleResizeIncrements),
Binding::new("R", ModifiersState::CONTROL, Action::ToggleResizable),
// M.
Binding::new("M", ModifiersState::CONTROL, Action::ToggleMaximize),
Binding::new("M", ModifiersState::ALT, Action::Minimize),
// N.
Binding::new("N", ModifiersState::CONTROL, Action::CreateNewWindow),
// C.
Binding::new("C", ModifiersState::CONTROL, Action::NextCursor),
Binding::new("C", ModifiersState::ALT, Action::NextCustomCursor),
Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility),
#[cfg(macos_platform)]
Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab),
#[cfg(macos_platform)]
Binding::new("O", ModifiersState::CONTROL, Action::CycleOptionAsAlt),
];
const MOUSE_BINDINGS: &[Binding<MouseButton>] = &[
Binding::new(
MouseButton::Left,
ModifiersState::ALT,
Action::DragResizeWindow,
),
Binding::new(
MouseButton::Left,
ModifiersState::CONTROL,
Action::DragWindow,
),
Binding::new(
MouseButton::Right,
ModifiersState::CONTROL,
Action::ShowWindowMenu,
),
];

62
examples/monitors.rs Normal file
View File

@@ -0,0 +1,62 @@
use std::error::Error;
use winit::{
event::{Event, StartCause},
event_loop::{ActiveEventLoop, EventLoop},
};
fn main() -> Result<(), Box<dyn Error>> {
let event_loop = EventLoop::new()?;
Ok(event_loop.run(|event, event_loop| match event {
Event::NewEvents(StartCause::Init) => {
dump_monitors(event_loop);
event_loop.exit()
}
_ => {}
})?)
}
fn dump_monitors(event_loop: &ActiveEventLoop) {
println!("Monitors information");
let primary_monitor = event_loop.primary_monitor();
for monitor in event_loop.available_monitors() {
let intro = if primary_monitor.as_ref() == Some(&monitor) {
"Primary monitor"
} else {
"Monitor"
};
if let Some(name) = monitor.name() {
println!("{intro}: {name}");
} else {
println!("{intro}: [no name]");
}
let size = monitor.size();
print!(" Current mode: {}x{}", size.width, size.height);
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
} else {
println!();
}
let position = monitor.position();
println!(" Position: {}, {}", position.x, position.y);
println!(" Scale factor: {}", monitor.scale_factor());
println!(" Available modes (width x height x bit-depth):");
for mode in monitor.video_modes() {
let size = mode.size();
let m_hz = mode.refresh_rate_millihertz();
println!(
" {:04}x{:04}x{:02} @ {:>3}.{} Hz",
size.width,
size.height,
mode.bit_depth(),
m_hz / 1000,
m_hz % 1000
);
}
}
}

View File

@@ -11,59 +11,51 @@
fn main() -> std::process::ExitCode { fn main() -> std::process::ExitCode {
use std::{process::ExitCode, thread::sleep, time::Duration}; use std::{process::ExitCode, thread::sleep, time::Duration};
use winit::application::ApplicationHandler; use simple_logger::SimpleLogger;
use winit::event::WindowEvent; use winit::{
use winit::event_loop::{ActiveEventLoop, EventLoop}; event::{Event, WindowEvent},
use winit::platform::pump_events::{EventLoopExtPumpEvents, PumpStatus}; event_loop::EventLoop,
use winit::window::{Window, WindowId}; platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
window::Window,
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[derive(Default)]
struct PumpDemo {
window: Option<Window>,
}
impl ApplicationHandler for PumpDemo {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes().with_title("A fantastic window!");
self.window = Some(event_loop.create_window(window_attributes).unwrap());
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
println!("{event:?}");
let window = match self.window.as_ref() {
Some(window) => window,
None => return,
};
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => {
fill::fill_window(window);
window.request_redraw();
}
_ => (),
}
}
}
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
tracing_subscriber::fmt::init(); SimpleLogger::new().init().unwrap();
let mut app = PumpDemo::default(); let mut window = None;
loop { loop {
let timeout = Some(Duration::ZERO); let timeout = Some(Duration::ZERO);
let status = event_loop.pump_app_events(timeout, &mut app); let status = event_loop.pump_events(timeout, |event, event_loop| {
if let Event::WindowEvent { event, .. } = &event {
// Print only Window events to reduce noise
println!("{event:?}");
}
match event {
Event::Resumed => {
let window_attributes =
Window::default_attributes().with_title("A fantastic window!");
window = Some(event_loop.create_window(window_attributes).unwrap());
}
Event::WindowEvent { event, .. } => {
let window = window.as_ref().unwrap();
match event {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => fill::fill_window(window),
_ => (),
}
}
Event::AboutToWait => {
window.as_ref().unwrap().request_redraw();
}
_ => (),
}
});
if let PumpStatus::Exit(exit_code) = status { if let PumpStatus::Exit(exit_code) = status {
break ExitCode::from(exit_code as u8); break ExitCode::from(exit_code as u8);

View File

@@ -2,94 +2,88 @@
// Limit this example to only compatible platforms. // Limit this example to only compatible platforms.
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))] #[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))]
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), impl std::error::Error> {
use std::time::Duration; use std::time::Duration;
use winit::application::ApplicationHandler; use simple_logger::SimpleLogger;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::{
use winit::platform::run_on_demand::EventLoopExtRunOnDemand; error::EventLoopError,
use winit::window::{Window, WindowId}; event::{Event, WindowEvent},
event_loop::EventLoop,
platform::run_on_demand::EventLoopExtRunOnDemand,
window::{Window, WindowId},
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
#[derive(Default)] #[derive(Default)]
struct App { struct App {
idx: usize,
window_id: Option<WindowId>, window_id: Option<WindowId>,
window: Option<Window>, window: Option<Window>,
} }
impl ApplicationHandler for App { SimpleLogger::new().init().unwrap();
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
if let Some(window) = self.window.as_ref() {
window.request_redraw();
}
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let window_attributes = Window::default_attributes()
.with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0));
let window = event_loop.create_window(window_attributes).unwrap();
self.window_id = Some(window.id());
self.window = Some(window);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
if event == WindowEvent::Destroyed && self.window_id == Some(window_id) {
println!(
"--------------------------------------------------------- Window {} Destroyed",
self.idx
);
self.window_id = None;
event_loop.exit();
return;
}
let window = match self.window.as_mut() {
Some(window) => window,
None => return,
};
match event {
WindowEvent::CloseRequested => {
println!("--------------------------------------------------------- Window {} CloseRequested", self.idx);
fill::cleanup_window(window);
self.window = None;
}
WindowEvent::RedrawRequested => {
fill::fill_window(window);
}
_ => (),
}
}
}
tracing_subscriber::fmt::init();
let mut event_loop = EventLoop::new().unwrap(); let mut event_loop = EventLoop::new().unwrap();
let mut app = App { fn run_app(event_loop: &mut EventLoop<()>, idx: usize) -> Result<(), EventLoopError> {
idx: 1, let mut app = App::default();
..Default::default()
}; event_loop.run_on_demand(move |event, event_loop| {
event_loop.run_app_on_demand(&mut app)?; println!("Run {idx}: {:?}", event);
if let Some(window) = &app.window {
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
} if window.id() == window_id => {
println!("--------------------------------------------------------- Window {idx} CloseRequested");
fill::cleanup_window(window);
app.window = None;
}
Event::AboutToWait => window.request_redraw(),
Event::WindowEvent {
event: WindowEvent::RedrawRequested,
..
} => {
fill::fill_window(window);
}
_ => (),
}
} else if let Some(id) = app.window_id {
match event {
Event::WindowEvent {
event: WindowEvent::Destroyed,
window_id,
} if id == window_id => {
println!("--------------------------------------------------------- Window {idx} Destroyed");
app.window_id = None;
event_loop.exit();
}
_ => (),
}
} else if let Event::Resumed = event {
let window_attributes = Window::default_attributes()
.with_title("Fantastic window number one!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0));
let window = event_loop.create_window(window_attributes).unwrap();
app.window_id = Some(window.id());
app.window = Some(window);
}
})
}
run_app(&mut event_loop, 1)?;
println!("--------------------------------------------------------- Finished first loop"); println!("--------------------------------------------------------- Finished first loop");
println!("--------------------------------------------------------- Waiting 5 seconds"); println!("--------------------------------------------------------- Waiting 5 seconds");
std::thread::sleep(Duration::from_secs(5)); std::thread::sleep(Duration::from_secs(5));
app.idx += 1; let ret = run_app(&mut event_loop, 2);
event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished second loop"); println!("--------------------------------------------------------- Finished second loop");
Ok(()) ret
} }
#[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))] #[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))]

View File

@@ -1,984 +0,0 @@
//! Simple winit application.
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fmt::Debug;
#[cfg(not(any(android_platform, ios_platform)))]
use std::num::NonZeroU32;
use std::path::Path;
use cursor_icon::CursorIcon;
#[cfg(not(any(android_platform, ios_platform)))]
use rwh_05::HasRawDisplayHandle;
#[cfg(not(any(android_platform, ios_platform)))]
use softbuffer::{Context, Surface};
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event::{DeviceEvent, DeviceId, Ime, WindowEvent};
use winit::event::{MouseButton, MouseScrollDelta};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{Key, ModifiersState};
use winit::window::{
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
Theme,
};
use winit::window::{Window, WindowId};
#[cfg(macos_platform)]
use winit::platform::macos::{OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS};
#[cfg(any(x11_platform, wayland_platform))]
use winit::platform::startup_notify::{
self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify,
};
/// The amount of points to around the window for drag resize direction calculations.
const BORDER_SIZE: f64 = 20.;
fn main() -> Result<(), Box<dyn Error>> {
let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
let _event_loop_proxy = event_loop.create_proxy();
// Wire the user event from another thread.
#[cfg(not(web_platform))]
std::thread::spawn(move || {
// Wake up the `event_loop` once every second and dispatch a custom event
// from a different thread.
println!("Starting to send user event every second");
loop {
let _ = _event_loop_proxy.send_event(UserEvent::WakeUp);
std::thread::sleep(std::time::Duration::from_secs(1));
}
});
let mut state = Application::new(&event_loop);
event_loop.run_app(&mut state).map_err(Into::into)
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
enum UserEvent {
WakeUp,
}
/// Application state and event handling.
struct Application {
/// Custom cursors assets.
custom_cursors: Vec<CustomCursor>,
/// Application icon.
icon: Icon,
windows: HashMap<WindowId, WindowState>,
/// Drawing context.
///
/// With OpenGL it could be EGLDisplay.
#[cfg(not(any(android_platform, ios_platform)))]
context: Option<Context>,
}
impl Application {
fn new<T>(event_loop: &EventLoop<T>) -> Self {
// SAFETY: we drop the context right before the event loop is stopped, thus making it safe.
#[cfg(not(any(android_platform, ios_platform)))]
let context = Some(unsafe { Context::from_raw(event_loop.raw_display_handle()).unwrap() });
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
// since it seems to work well enough in most cases. Be careful about going too high, or
// you'll be bitten by the low-quality downscaling built into the WM.
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/data/icon.png");
let icon = load_icon(Path::new(path));
println!("Loading cursor assets");
let custom_cursors = vec![
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))),
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross2.png"))),
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/gradient.png"))),
];
Self {
#[cfg(not(any(android_platform, ios_platform)))]
context,
custom_cursors,
icon,
windows: Default::default(),
}
}
fn create_window(
&mut self,
event_loop: &ActiveEventLoop,
_tab_id: Option<String>,
) -> Result<WindowId, Box<dyn Error>> {
// TODO read-out activation token.
#[allow(unused_mut)]
let mut window_attributes = Window::default_attributes()
.with_title("Winit window")
.with_transparent(true)
.with_window_icon(Some(self.icon.clone()));
#[cfg(any(x11_platform, wayland_platform))]
if let Some(token) = event_loop.read_token_from_env() {
startup_notify::reset_activation_token_env();
println!("Using token {:?} to activate a window", token);
window_attributes = window_attributes.with_activation_token(token);
}
#[cfg(macos_platform)]
if let Some(tab_id) = _tab_id {
window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
}
let window = event_loop.create_window(window_attributes)?;
#[cfg(ios_platform)]
{
use winit::platform::ios::WindowExtIOS;
window.recognize_doubletap_gesture(true);
window.recognize_pinch_gesture(true);
window.recognize_rotation_gesture(true);
}
let window_state = WindowState::new(self, window)?;
let window_id = window_state.window.id();
println!("Created new window with id={window_id:?}");
self.windows.insert(window_id, window_state);
Ok(window_id)
}
fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
// let cursor_position = self.cursor_position;
let window = self.windows.get_mut(&window_id).unwrap();
println!("Executing action: {action:?}");
match action {
Action::CloseWindow => {
let _ = self.windows.remove(&window_id);
}
Action::CreateNewWindow => {
#[cfg(any(x11_platform, wayland_platform))]
if let Err(err) = window.window.request_activation_token() {
println!("Failed to get activation token: {err}");
} else {
return;
}
if let Err(err) = self.create_window(event_loop, None) {
eprintln!("Error creating new window: {err}");
}
}
Action::ToggleResizeIncrements => window.toggle_resize_increments(),
Action::ToggleCursorVisibility => window.toggle_cursor_visibility(),
Action::ToggleResizable => window.toggle_resizable(),
Action::ToggleDecorations => window.toggle_decorations(),
Action::ToggleFullscreen => window.toggle_fullscreen(),
Action::ToggleMaximize => window.toggle_maximize(),
Action::ToggleImeInput => window.toggle_ime(),
Action::Minimize => window.minimize(),
Action::NextCursor => window.next_cursor(),
Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors),
Action::CycleCursorGrab => window.cycle_cursor_grab(),
Action::DragWindow => window.drag_window(),
Action::DragResizeWindow => window.drag_resize_window(),
Action::ShowWindowMenu => window.show_menu(),
Action::PrintHelp => self.print_help(),
#[cfg(macos_platform)]
Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
#[cfg(macos_platform)]
Action::CreateNewTab => {
let tab_id = window.window.tabbing_identifier();
if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
eprintln!("Error creating new window: {err}");
}
}
}
}
fn dump_monitors(&self, event_loop: &ActiveEventLoop) {
println!("Monitors information");
let primary_monitor = event_loop.primary_monitor();
for monitor in event_loop.available_monitors() {
let intro = if primary_monitor.as_ref() == Some(&monitor) {
"Primary monitor"
} else {
"Monitor"
};
if let Some(name) = monitor.name() {
println!("{intro}: {name}");
} else {
println!("{intro}: [no name]");
}
let PhysicalSize { width, height } = monitor.size();
print!(" Current mode: {width}x{height}");
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
} else {
println!();
}
let PhysicalPosition { x, y } = monitor.position();
println!(" Position: {x},{y}");
println!(" Scale factor: {}", monitor.scale_factor());
println!(" Available modes (width x height x bit-depth):");
for mode in monitor.video_modes() {
let PhysicalSize { width, height } = mode.size();
let bits = mode.bit_depth();
let m_hz = mode.refresh_rate_millihertz();
println!(
" {width}x{height}x{bits} @ {}.{} Hz",
m_hz / 1000,
m_hz % 1000
);
}
}
}
/// Process the key binding.
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
KEY_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&key, mods)
.then_some(binding.action)
})
}
/// Process mouse binding.
fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
MOUSE_BINDINGS.iter().find_map(|binding| {
binding
.is_triggered_by(&button, mods)
.then_some(binding.action)
})
}
fn print_help(&self) {
println!("Keyboard bindings:");
for binding in KEY_BINDINGS {
println!(
"{}{:<10} - {} ({})",
modifiers_to_string(binding.mods),
binding.trigger,
binding.action,
binding.action.help(),
);
}
println!("Mouse bindings:");
for binding in MOUSE_BINDINGS {
println!(
"{}{:<10} - {} ({})",
modifiers_to_string(binding.mods),
mouse_button_to_string(binding.trigger),
binding.action,
binding.action.help(),
);
}
}
}
impl ApplicationHandler<UserEvent> for Application {
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
println!("User event: {event:?}");
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
let window = match self.windows.get_mut(&window_id) {
Some(window) => window,
None => return,
};
match event {
WindowEvent::Resized(size) => {
window.resize(size);
}
WindowEvent::Focused(focused) => {
if focused {
println!("Window={window_id:?} fosused");
} else {
println!("Window={window_id:?} unfosused");
}
}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
println!("Window={window_id:?} changed scale to {scale_factor}");
}
WindowEvent::ThemeChanged(theme) => {
println!("Theme changed to {theme:?}");
window.set_theme(theme);
}
WindowEvent::RedrawRequested => {
if let Err(err) = window.draw() {
eprintln!("Error drawing window: {err}");
}
}
WindowEvent::Occluded(occluded) => {
window.set_occluded(occluded);
}
WindowEvent::CloseRequested => {
println!("Closing Window={window_id:?}");
self.windows.remove(&window_id);
}
WindowEvent::ModifiersChanged(modifiers) => {
window.modifiers = modifiers.state();
println!("Modifiers changed to {:?}", window.modifiers);
}
WindowEvent::MouseWheel { delta, .. } => match delta {
MouseScrollDelta::LineDelta(x, y) => {
println!("Mouse wheel Line Delta: ({x},{y})");
}
MouseScrollDelta::PixelDelta(px) => {
println!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y);
}
},
WindowEvent::KeyboardInput {
event,
is_synthetic: false,
..
} => {
let mods = window.modifiers;
// Dispatch actions only on press.
if event.state.is_pressed() {
let action = if let Key::Character(ch) = event.logical_key.as_ref() {
Self::process_key_binding(&ch.to_uppercase(), &mods)
} else {
None
};
if let Some(action) = action {
self.handle_action(event_loop, window_id, action);
}
}
}
WindowEvent::MouseInput { button, state, .. } => {
let mods = window.modifiers;
if let Some(action) = state
.is_pressed()
.then(|| Self::process_mouse_binding(button, &mods))
.flatten()
{
self.handle_action(event_loop, window_id, action);
}
}
WindowEvent::CursorLeft { .. } => {
println!("Cursor left Window={window_id:?}");
window.cursor_left();
}
WindowEvent::CursorMoved { position, .. } => {
println!("Moved cursor to {position:?}");
window.cursor_moved(position);
}
WindowEvent::ActivationTokenDone { token: _token, .. } => {
#[cfg(any(x11_platform, wayland_platform))]
{
startup_notify::set_activation_token_env(_token);
if let Err(err) = self.create_window(event_loop, None) {
eprintln!("Error creating new window: {err}");
}
}
}
WindowEvent::Ime(event) => match event {
Ime::Enabled => println!("IME enabled for Window={window_id:?}"),
Ime::Preedit(text, caret_pos) => {
println!("Preedit: {}, with caret at {:?}", text, caret_pos);
}
Ime::Commit(text) => {
println!("Committed: {}", text);
}
Ime::Disabled => println!("IME disabled for Window={window_id:?}"),
},
WindowEvent::PinchGesture { delta, .. } => {
window.zoom += delta;
let zoom = window.zoom;
if delta > 0.0 {
println!("Zoomed in {delta:.5} (now: {zoom:.5})");
} else {
println!("Zoomed out {delta:.5} (now: {zoom:.5})");
}
}
WindowEvent::RotationGesture { delta, .. } => {
window.rotated += delta;
let rotated = window.rotated;
if delta > 0.0 {
println!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
} else {
println!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
}
}
WindowEvent::DoubleTapGesture { .. } => {
println!("Smart zoom");
}
WindowEvent::TouchpadPressure { .. }
| WindowEvent::HoveredFileCancelled
| WindowEvent::KeyboardInput { .. }
| WindowEvent::CursorEntered { .. }
| WindowEvent::AxisMotion { .. }
| WindowEvent::DroppedFile(_)
| WindowEvent::HoveredFile(_)
| WindowEvent::Destroyed
| WindowEvent::Touch(_)
| WindowEvent::Moved(_) => (),
}
}
fn device_event(
&mut self,
_event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
println!("Device {device_id:?} event: {event:?}");
}
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
println!("Resumed the event loop");
self.dump_monitors(event_loop);
// Create initial window.
self.create_window(event_loop, None)
.expect("failed to create initial window");
self.print_help();
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
if self.windows.is_empty() {
println!("No windows left, exiting...");
event_loop.exit();
}
}
#[cfg(not(any(android_platform, ios_platform)))]
fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
// We must drop the context here.
self.context = None;
}
}
/// State of the window.
struct WindowState {
/// IME input.
ime: bool,
/// Render surface.
///
/// NOTE: This surface must be dropped before the `Window`.
#[cfg(not(any(android_platform, ios_platform)))]
surface: Surface,
/// The actual winit Window.
window: Window,
/// The window theme we're drawing with.
theme: Theme,
/// Cursor position over the window.
cursor_position: Option<PhysicalPosition<f64>>,
/// Window modifiers state.
modifiers: ModifiersState,
/// Occlusion state of the window.
occluded: bool,
/// Current cursor grab mode.
cursor_grab: CursorGrabMode,
/// The amount of zoom into window.
zoom: f64,
/// The amount of rotation of the window.
rotated: f32,
#[cfg(macos_platform)]
option_as_alt: OptionAsAlt,
// Cursor states.
named_idx: usize,
custom_idx: usize,
cursor_hidden: bool,
}
impl WindowState {
fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
// SAFETY: the surface is dropped before the `window` which provided it with handle, thus
// it doesn't outlive it.
#[cfg(not(any(android_platform, ios_platform)))]
let surface = unsafe { Surface::new(app.context.as_ref().unwrap(), &window)? };
let theme = window.theme().unwrap_or(Theme::Dark);
println!("Theme: {theme:?}");
let named_idx = 0;
window.set_cursor(CURSORS[named_idx]);
// Allow IME out of the box.
let ime = true;
window.set_ime_allowed(ime);
let size = window.inner_size();
let mut state = Self {
#[cfg(macos_platform)]
option_as_alt: window.option_as_alt(),
custom_idx: app.custom_cursors.len() - 1,
cursor_grab: CursorGrabMode::None,
named_idx,
#[cfg(not(any(android_platform, ios_platform)))]
surface,
window,
theme,
ime,
cursor_position: Default::default(),
cursor_hidden: Default::default(),
modifiers: Default::default(),
occluded: Default::default(),
rotated: Default::default(),
zoom: Default::default(),
};
state.resize(size);
Ok(state)
}
pub fn toggle_ime(&mut self) {
self.ime = !self.ime;
self.window.set_ime_allowed(self.ime);
if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
self.window
.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
}
}
pub fn minimize(&mut self) {
self.window.set_minimized(true);
}
pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
self.cursor_position = Some(position);
if self.ime {
self.window
.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
}
}
pub fn cursor_left(&mut self) {
self.cursor_position = None;
}
/// Toggle maximized.
fn toggle_maximize(&self) {
let maximized = self.window.is_maximized();
self.window.set_maximized(!maximized);
}
/// Toggle window decorations.
fn toggle_decorations(&self) {
let decorated = self.window.is_decorated();
self.window.set_decorations(!decorated);
}
/// Toggle window resizable state.
fn toggle_resizable(&self) {
let resizable = self.window.is_resizable();
self.window.set_resizable(!resizable);
}
/// Toggle cursor visibility
fn toggle_cursor_visibility(&mut self) {
self.cursor_hidden = !self.cursor_hidden;
self.window.set_cursor_visible(!self.cursor_hidden);
}
/// Toggle resize increments on a window.
fn toggle_resize_increments(&mut self) {
let new_increments = match self.window.resize_increments() {
Some(_) => None,
None => Some(LogicalSize::new(25.0, 25.0)),
};
println!("Had increments: {}", new_increments.is_none());
self.window.set_resize_increments(new_increments);
}
/// Toggle fullscreen.
fn toggle_fullscreen(&self) {
let fullscreen = if self.window.fullscreen().is_some() {
None
} else {
Some(Fullscreen::Borderless(None))
};
self.window.set_fullscreen(fullscreen);
}
/// Cycle through the grab modes ignoring errors.
fn cycle_cursor_grab(&mut self) {
self.cursor_grab = match self.cursor_grab {
CursorGrabMode::None => CursorGrabMode::Confined,
CursorGrabMode::Confined => CursorGrabMode::Locked,
CursorGrabMode::Locked => CursorGrabMode::None,
};
println!("Changing cursor grab mode to {:?}", self.cursor_grab);
if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) {
eprintln!("Error setting cursor grab: {err}");
}
}
#[cfg(macos_platform)]
fn cycle_option_as_alt(&mut self) {
self.option_as_alt = match self.option_as_alt {
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
OptionAsAlt::Both => OptionAsAlt::None,
};
println!("Setting option as alt {:?}", self.option_as_alt);
self.window.set_option_as_alt(self.option_as_alt);
}
/// Pick the next cursor.
fn next_cursor(&mut self) {
self.named_idx = (self.named_idx + 1) % CURSORS.len();
println!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]);
self.window
.set_cursor(Cursor::Icon(CURSORS[self.named_idx]));
}
/// Pick the next custom cursor.
fn next_custom_cursor(&mut self, custom_cursors: &[CustomCursor]) {
self.custom_idx = (self.custom_idx + 1) % custom_cursors.len();
let cursor = Cursor::Custom(custom_cursors[self.custom_idx].clone());
self.window.set_cursor(cursor);
}
/// Resize the window to the new size.
fn resize(&mut self, _size: PhysicalSize<u32>) {
#[cfg(not(any(android_platform, ios_platform)))]
{
let (width, height) =
match (NonZeroU32::new(_size.width), NonZeroU32::new(_size.height)) {
(Some(width), Some(height)) => (width, height),
_ => return,
};
self.surface
.resize(width, height)
.expect("failed to resize inner buffer");
}
self.window.request_redraw();
}
/// Change the theme.
fn set_theme(&mut self, theme: Theme) {
self.theme = theme;
self.window.request_redraw();
}
/// Show window menu.
fn show_menu(&self) {
if let Some(position) = self.cursor_position {
self.window.show_window_menu(position);
}
}
/// Drag the window.
fn drag_window(&self) {
if let Err(err) = self.window.drag_window() {
println!("Error starting window drag: {err}");
} else {
println!("Dragging window Window={:?}", self.window.id());
}
}
/// Drag-resize the window.
fn drag_resize_window(&self) {
let position = match self.cursor_position {
Some(position) => position,
None => {
println!("Drag-resize requires cursor to be inside the window");
return;
}
};
let win_size = self.window.inner_size();
let border_size = BORDER_SIZE * self.window.scale_factor();
let x_direction = if position.x < border_size {
ResizeDirection::West
} else if position.x > (win_size.width as f64 - border_size) {
ResizeDirection::East
} else {
// Use arbitrary direction instead of None for simplicity.
ResizeDirection::SouthEast
};
let y_direction = if position.y < border_size {
ResizeDirection::North
} else if position.y > (win_size.height as f64 - border_size) {
ResizeDirection::South
} else {
// Use arbitrary direction instead of None for simplicity.
ResizeDirection::SouthEast
};
let direction = match (x_direction, y_direction) {
(ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
(ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
(ResizeDirection::West, _) => ResizeDirection::West,
(ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
(ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
(ResizeDirection::East, _) => ResizeDirection::East,
(_, ResizeDirection::South) => ResizeDirection::South,
(_, ResizeDirection::North) => ResizeDirection::North,
_ => return,
};
if let Err(err) = self.window.drag_resize_window(direction) {
println!("Error starting window drag-resize: {err}");
} else {
println!("Drag-resizing window Window={:?}", self.window.id());
}
}
/// Change window occlusion state.
fn set_occluded(&mut self, occluded: bool) {
self.occluded = occluded;
if !occluded {
self.window.request_redraw();
}
}
/// Draw the window contents.
#[cfg(not(any(android_platform, ios_platform)))]
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
if self.occluded {
println!("Skipping drawing occluded window={:?}", self.window.id());
return Ok(());
}
const WHITE: u32 = 0xFFFFFFFF;
const DARK_GRAY: u32 = 0xFF181818;
let color = match self.theme {
Theme::Light => WHITE,
Theme::Dark => DARK_GRAY,
};
let mut buffer = self.surface.buffer_mut()?;
buffer.fill(color);
self.window.pre_present_notify();
buffer.present()?;
Ok(())
}
#[cfg(any(android_platform, ios_platform))]
fn draw(&mut self) -> Result<(), Box<dyn Error>> {
println!("Drawing but without rendering...");
Ok(())
}
}
struct Binding<T: Eq> {
trigger: T,
mods: ModifiersState,
action: Action,
}
impl<T: Eq> Binding<T> {
const fn new(trigger: T, mods: ModifiersState, action: Action) -> Self {
Self {
trigger,
mods,
action,
}
}
fn is_triggered_by(&self, trigger: &T, mods: &ModifiersState) -> bool {
&self.trigger == trigger && &self.mods == mods
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Action {
CloseWindow,
ToggleCursorVisibility,
CreateNewWindow,
ToggleResizeIncrements,
ToggleImeInput,
ToggleDecorations,
ToggleResizable,
ToggleFullscreen,
ToggleMaximize,
Minimize,
NextCursor,
NextCustomCursor,
CycleCursorGrab,
PrintHelp,
DragWindow,
DragResizeWindow,
ShowWindowMenu,
#[cfg(macos_platform)]
CycleOptionAsAlt,
#[cfg(macos_platform)]
CreateNewTab,
}
impl Action {
fn help(&self) -> &'static str {
match self {
Action::CloseWindow => "Close window",
Action::ToggleCursorVisibility => "Hide cursor",
Action::CreateNewWindow => "Create new window",
Action::ToggleImeInput => "Toggle IME input",
Action::ToggleDecorations => "Toggle decorations",
Action::ToggleResizable => "Toggle window resizable state",
Action::ToggleFullscreen => "Toggle fullscreen",
Action::ToggleMaximize => "Maximize",
Action::Minimize => "Minimize",
Action::ToggleResizeIncrements => "Use resize increments when resizing window",
Action::NextCursor => "Advance the cursor to the next value",
Action::NextCustomCursor => "Advance custom cursor to the next value",
Action::CycleCursorGrab => "Cycle through cursor grab mode",
Action::PrintHelp => "Print help",
Action::DragWindow => "Start window drag",
Action::DragResizeWindow => "Start window drag-resize",
Action::ShowWindowMenu => "Show window menu",
#[cfg(macos_platform)]
Action::CycleOptionAsAlt => "Cycle option as alt mode",
#[cfg(macos_platform)]
Action::CreateNewTab => "Create new tab",
}
}
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self, f)
}
}
fn decode_cursor(bytes: &[u8]) -> CustomCursorSource {
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
let samples = img.into_flat_samples();
let (_, w, h) = samples.extents();
let (w, h) = (w as u16, h as u16);
CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap()
}
fn load_icon(path: &Path) -> Icon {
let (icon_rgba, icon_width, icon_height) = {
let image = image::open(path)
.expect("Failed to open icon path")
.into_rgba8();
let (width, height) = image.dimensions();
let rgba = image.into_raw();
(rgba, width, height)
};
Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
}
fn modifiers_to_string(mods: ModifiersState) -> String {
let mut mods_line = String::new();
// Always add + since it's printed as a part of the bindings.
for (modifier, desc) in [
(ModifiersState::SUPER, "Super+"),
(ModifiersState::ALT, "Alt+"),
(ModifiersState::CONTROL, "Ctrl+"),
(ModifiersState::SHIFT, "Shift+"),
] {
if !mods.contains(modifier) {
continue;
}
mods_line.push_str(desc);
}
mods_line
}
fn mouse_button_to_string(button: MouseButton) -> &'static str {
match button {
MouseButton::Left => "LMB",
MouseButton::Right => "RMB",
MouseButton::Middle => "MMB",
MouseButton::Back => "Back",
MouseButton::Forward => "Forward",
MouseButton::Other(_) => "",
}
}
/// Cursor list to cycle through.
const CURSORS: &[CursorIcon] = &[
CursorIcon::Default,
CursorIcon::Crosshair,
CursorIcon::Pointer,
CursorIcon::Move,
CursorIcon::Text,
CursorIcon::Wait,
CursorIcon::Help,
CursorIcon::Progress,
CursorIcon::NotAllowed,
CursorIcon::ContextMenu,
CursorIcon::Cell,
CursorIcon::VerticalText,
CursorIcon::Alias,
CursorIcon::Copy,
CursorIcon::NoDrop,
CursorIcon::Grab,
CursorIcon::Grabbing,
CursorIcon::AllScroll,
CursorIcon::ZoomIn,
CursorIcon::ZoomOut,
CursorIcon::EResize,
CursorIcon::NResize,
CursorIcon::NeResize,
CursorIcon::NwResize,
CursorIcon::SResize,
CursorIcon::SeResize,
CursorIcon::SwResize,
CursorIcon::WResize,
CursorIcon::EwResize,
CursorIcon::NsResize,
CursorIcon::NeswResize,
CursorIcon::NwseResize,
CursorIcon::ColResize,
CursorIcon::RowResize,
];
const KEY_BINDINGS: &[Binding<&'static str>] = &[
Binding::new("Q", ModifiersState::CONTROL, Action::CloseWindow),
Binding::new("H", ModifiersState::CONTROL, Action::PrintHelp),
Binding::new("F", ModifiersState::CONTROL, Action::ToggleFullscreen),
Binding::new("D", ModifiersState::CONTROL, Action::ToggleDecorations),
Binding::new("I", ModifiersState::CONTROL, Action::ToggleImeInput),
Binding::new("L", ModifiersState::CONTROL, Action::CycleCursorGrab),
Binding::new("P", ModifiersState::CONTROL, Action::ToggleResizeIncrements),
Binding::new("R", ModifiersState::CONTROL, Action::ToggleResizable),
// M.
Binding::new("M", ModifiersState::CONTROL, Action::ToggleMaximize),
Binding::new("M", ModifiersState::ALT, Action::Minimize),
// N.
Binding::new("N", ModifiersState::CONTROL, Action::CreateNewWindow),
// C.
Binding::new("C", ModifiersState::CONTROL, Action::NextCursor),
Binding::new("C", ModifiersState::ALT, Action::NextCustomCursor),
Binding::new("Z", ModifiersState::CONTROL, Action::ToggleCursorVisibility),
#[cfg(macos_platform)]
Binding::new("T", ModifiersState::SUPER, Action::CreateNewTab),
#[cfg(macos_platform)]
Binding::new("O", ModifiersState::CONTROL, Action::CycleOptionAsAlt),
];
const MOUSE_BINDINGS: &[Binding<MouseButton>] = &[
Binding::new(
MouseButton::Left,
ModifiersState::ALT,
Action::DragResizeWindow,
),
Binding::new(
MouseButton::Left,
ModifiersState::CONTROL,
Action::DragWindow,
),
Binding::new(
MouseButton::Right,
ModifiersState::CONTROL,
Action::ShowWindowMenu,
),
];

View File

@@ -3,37 +3,40 @@ use std::error::Error;
#[cfg(x11_platform)] #[cfg(x11_platform)]
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
use winit::application::ApplicationHandler; use simple_logger::SimpleLogger;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::{
use winit::platform::x11::WindowAttributesExtX11; event::{Event, WindowEvent},
use winit::window::{Window, WindowId}; event_loop::EventLoop,
platform::x11::WindowAttributesExtX11,
window::Window,
};
#[path = "util/fill.rs"] #[path = "util/fill.rs"]
mod fill; mod fill;
pub struct XEmbedDemo { // First argument should be a 32-bit X11 window ID.
parent_window_id: u32, let parent_window_id = std::env::args()
window: Option<Window>, .nth(1)
} .ok_or("Expected a 32-bit X11 window ID as the first argument.")?
.parse::<u32>()?;
impl ApplicationHandler for XEmbedDemo { SimpleLogger::new().init().unwrap();
fn resumed(&mut self, event_loop: &ActiveEventLoop) { let event_loop = EventLoop::new()?;
let mut window = None;
event_loop.run(move |event, event_loop| match event {
Event::Resumed => {
let window_attributes = Window::default_attributes() let window_attributes = Window::default_attributes()
.with_title("An embedded window!") .with_title("An embedded window!")
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0)) .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
.with_embed_parent_window(self.parent_window_id); .with_embed_parent_window(parent_window_id);
self.window = Some(event_loop.create_window(window_attributes).unwrap()); window = Some(event_loop.create_window(window_attributes).unwrap());
} }
Event::WindowEvent { event, .. } => {
let window = window.as_ref().unwrap();
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
_window_id: WindowId,
event: WindowEvent,
) {
let window = self.window.as_ref().unwrap();
match event { match event {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
@@ -43,26 +46,13 @@ fn main() -> Result<(), Box<dyn Error>> {
_ => (), _ => (),
} }
} }
Event::AboutToWait => {
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) { window.as_ref().unwrap().request_redraw();
self.window.as_ref().unwrap().request_redraw();
} }
} _ => (),
})?;
// First argument should be a 32-bit X11 window ID. Ok(())
let parent_window_id = std::env::args()
.nth(1)
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
.parse::<u32>()?;
tracing_subscriber::fmt::init();
let event_loop = EventLoop::new()?;
let mut app = XEmbedDemo {
parent_window_id,
window: None,
};
event_loop.run_app(&mut app).map_err(Into::into)
} }
#[cfg(not(x11_platform))] #[cfg(not(x11_platform))]

View File

@@ -1,221 +0,0 @@
//! End user application handling.
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use crate::event_loop::ActiveEventLoop;
use crate::window::WindowId;
/// The handler of the application events.
pub trait ApplicationHandler<T: 'static = ()> {
/// Emitted when new events arrive from the OS to be processed.
///
/// This is a useful place to put code that should be done before you start processing
/// events, such as updating frame timing information for benchmarking or checking the
/// [`StartCause`] to see if a timer set by
/// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed.
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
let _ = (event_loop, cause);
}
/// Emitted when the application has been resumed.
///
/// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a
/// formal suspend/resume lifecycle. For systems without a formal suspend/resume lifecycle
/// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init]
/// event.
///
/// # Portability
///
/// It's recommended that applications should only initialize their graphics context and create
/// a window after they have received their first `Resumed` event. Some systems
/// (specifically Android) won't allow applications to create a render surface until they are
/// resumed.
///
/// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events.
///
/// Also see [`Suspended`] notes.
///
/// ## Android
///
/// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is
/// expected to closely correlate with the [`onResume`] lifecycle event but there may technically
/// be a discrepancy.
///
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
///
/// Applications that need to run on Android must wait until they have been `Resumed`
/// before they will be able to create a render surface (such as an `EGLSurface`,
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a
/// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their
/// render surfaces are invalid and should be dropped.
///
/// Also see [`Suspended`] notes.
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`]
/// callback which means the application is "active" (according to the
/// [iOS application lifecycle]).
///
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
/// with the property [`persisted`] being true, which means that the page is being
/// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
/// stores a complete snapshot of a page (including the JavaScript heap) as the
/// user is navigating away.
///
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
/// [`Suspended`]: Self::suspended
fn resumed(&mut self, event_loop: &ActiveEventLoop);
/// Emitted when an event is sent from [`EventLoopProxy::send_event`].
///
/// [`EventLoopProxy::send_event`]: crate::event_loop::EventLoopProxy::send_event
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
let _ = (event_loop, event);
}
/// Emitted when the OS sends an event to a winit window.
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
);
/// Emitted when the OS sends an event to a device.
fn device_event(
&mut self,
event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
let _ = (event_loop, device_id, event);
}
/// Emitted when the event loop is about to block and wait for new events.
///
/// Most applications shouldn't need to hook into this event since there is no real relationship
/// between how often the event loop needs to wake up and the dispatching of any specific events.
///
/// High frequency event sources, such as input devices could potentially lead to lots of wake
/// ups and also lots of corresponding `AboutToWait` events.
///
/// This is not an ideal event to drive application rendering from and instead applications
/// should render in response to [`WindowEvent::RedrawRequested`] events.
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application has been suspended.
///
/// # Portability
///
/// Not all platforms support the notion of suspending applications, and there may be no
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
/// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason,
/// Winit does not currently try to emit pseudo `Suspended` events before the application
/// quits on platforms without an application lifecycle.
///
/// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events.
///
/// Also see [`Resumed`] notes.
///
/// ## Android
///
/// On Android, the `Suspended` event is only sent when the application's associated
/// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`]
/// lifecycle event but there may technically be a discrepancy.
///
/// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause()
///
/// Applications that need to run on Android should assume their [`SurfaceView`] has been
/// destroyed, which indirectly invalidates any existing render surfaces that may have been
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
///
/// After being `Suspended` on Android applications must drop all render surfaces before
/// the event callback completes, which may be re-created when the application is next [`Resumed`].
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Suspended` event is currently emitted in response to an
/// [`applicationWillResignActive`] callback which means that the application is
/// about to transition from the active to inactive state (according to the
/// [iOS application lifecycle]).
///
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
/// with the property [`persisted`] being true, which means that the page is being
/// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a
/// complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
/// [`Resumed`]: Self::resumed
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the event loop is being shut down.
///
/// This is irreversible - if this method is called, it is guaranteed that the event loop
/// will exist right after.
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
/// Emitted when the application has received a memory warning.
///
/// ## Platform-specific
///
/// ### Android
///
/// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application
/// must [release memory] or risk being killed.
///
/// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory()
/// [release memory]: https://developer.android.com/topic/performance/memory#release
///
/// ### iOS
///
/// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`]
/// callback. The application must free as much memory as possible or risk being terminated, see
/// [how to respond to memory warnings].
///
/// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni
/// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings
///
/// ### Others
///
/// - **macOS / Orbital / Wayland / Web / Windows:** Unsupported.
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
}

View File

@@ -88,15 +88,6 @@ impl CustomCursor {
hotspot_x: u16, hotspot_x: u16,
hotspot_y: u16, hotspot_y: u16,
) -> Result<CustomCursorSource, BadImage> { ) -> Result<CustomCursorSource, BadImage> {
let _span = tracing::debug_span!(
"winit::Cursor::from_rgba",
width,
height,
hotspot_x,
hotspot_y
)
.entered();
Ok(CustomCursorSource { Ok(CustomCursorSource {
inner: PlatformCustomCursorSource::from_rgba( inner: PlatformCustomCursorSource::from_rgba(
rgba.into(), rgba.into(),

View File

@@ -1,37 +1,36 @@
//! The [`Event`] enum and assorted supporting types. //! The [`Event`] enum and assorted supporting types.
//! //!
//! These are sent to the closure given to [`EventLoop::run_app(...)`], where they get //! These are sent to the closure given to [`EventLoop::run(...)`], where they get
//! processed and used to modify the program state. For more details, see the root-level documentation. //! processed and used to modify the program state. For more details, see the root-level documentation.
//! //!
//! Some of these events represent different "parts" of a traditional event-handling loop. You could //! Some of these events represent different "parts" of a traditional event-handling loop. You could
//! approximate the basic ordering loop of [`EventLoop::run_app(...)`] like this: //! approximate the basic ordering loop of [`EventLoop::run(...)`] like this:
//! //!
//! ```rust,ignore //! ```rust,ignore
//! let mut start_cause = StartCause::Init; //! let mut start_cause = StartCause::Init;
//! //!
//! while !elwt.exiting() { //! while !elwt.exiting() {
//! app.new_events(event_loop, start_cause); //! event_handler(NewEvents(start_cause), elwt);
//! //!
//! for event in (window events, user events, device events) { //! for e in (window events, user events, device events) {
//! // This will pick the right method on the application based on the event. //! event_handler(e, elwt);
//! app.handle_event(event_loop, event);
//! } //! }
//! //!
//! for window_id in (redraw windows) { //! for w in (redraw windows) {
//! app.window_event(event_loop, window_id, RedrawRequested); //! event_handler(RedrawRequested(w), elwt);
//! } //! }
//! //!
//! app.about_to_wait(event_loop); //! event_handler(AboutToWait, elwt);
//! start_cause = wait_if_necessary(); //! start_cause = wait_if_necessary();
//! } //! }
//! //!
//! app.exiting(event_loop); //! event_handler(LoopExiting, elwt);
//! ``` //! ```
//! //!
//! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully //! This leaves out timing details like [`ControlFlow::WaitUntil`] but hopefully
//! describes what happens in what order. //! describes what happens in what order.
//! //!
//! [`EventLoop::run_app(...)`]: crate::event_loop::EventLoop::run_app //! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil //! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::{Mutex, Weak}; use std::sync::{Mutex, Weak};
@@ -60,55 +59,199 @@ use crate::{
/// See the module-level docs for more information on the event loop manages each event. /// See the module-level docs for more information on the event loop manages each event.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Event<T: 'static> { pub enum Event<T: 'static> {
/// See [`ApplicationHandler::new_events`] for details. /// Emitted when new events arrive from the OS to be processed.
/// ///
/// [`ApplicationHandler::new_events`]: crate::application::ApplicationHandler::new_events /// This event type is useful as a place to put code that should be done before you start
/// processing events, such as updating frame timing information for benchmarking or checking
/// the [`StartCause`] to see if a timer set by
/// [`ControlFlow::WaitUntil`](crate::event_loop::ControlFlow::WaitUntil) has elapsed.
NewEvents(StartCause), NewEvents(StartCause),
/// See [`ApplicationHandler::window_event`] for details. /// Emitted when the OS sends an event to a winit window.
///
/// [`ApplicationHandler::window_event`]: crate::application::ApplicationHandler::window_event
WindowEvent { WindowEvent {
window_id: WindowId, window_id: WindowId,
event: WindowEvent, event: WindowEvent,
}, },
/// See [`ApplicationHandler::device_event`] for details. /// Emitted when the OS sends an event to a device.
///
/// [`ApplicationHandler::device_event`]: crate::application::ApplicationHandler::device_event
DeviceEvent { DeviceEvent {
device_id: DeviceId, device_id: DeviceId,
event: DeviceEvent, event: DeviceEvent,
}, },
/// See [`ApplicationHandler::user_event`] for details. /// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event)
///
/// [`ApplicationHandler::user_event`]: crate::application::ApplicationHandler::user_event
UserEvent(T), UserEvent(T),
/// See [`ApplicationHandler::suspended`] for details. /// Emitted when the application has been suspended.
/// ///
/// [`ApplicationHandler::suspended`]: crate::application::ApplicationHandler::suspended /// # Portability
///
/// Not all platforms support the notion of suspending applications, and there may be no
/// technical way to guarantee being able to emit a `Suspended` event if the OS has
/// no formal application lifecycle (currently only Android, iOS, and Web do). For this reason,
/// Winit does not currently try to emit pseudo `Suspended` events before the application
/// quits on platforms without an application lifecycle.
///
/// Considering that the implementation of `Suspended` and [`Resumed`] events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) `Suspended` or [`Resumed`] events.
///
/// Also see [`Resumed`] notes.
///
/// ## Android
///
/// On Android, the `Suspended` event is only sent when the application's associated
/// [`SurfaceView`] is destroyed. This is expected to closely correlate with the [`onPause`]
/// lifecycle event but there may technically be a discrepancy.
///
/// [`onPause`]: https://developer.android.com/reference/android/app/Activity#onPause()
///
/// Applications that need to run on Android should assume their [`SurfaceView`] has been
/// destroyed, which indirectly invalidates any existing render surfaces that may have been
/// created outside of Winit (such as an `EGLSurface`, [`VkSurfaceKHR`] or [`wgpu::Surface`]).
///
/// After being `Suspended` on Android applications must drop all render surfaces before
/// the event callback completes, which may be re-created when the application is next [`Resumed`].
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Suspended` event is currently emitted in response to an
/// [`applicationWillResignActive`] callback which means that the application is
/// about to transition from the active to inactive state (according to the
/// [iOS application lifecycle]).
///
/// [`applicationWillResignActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Suspended` event is emitted in response to a [`pagehide`] event
/// with the property [`persisted`] being true, which means that the page is being
/// put in the [`bfcache`] (back/forward cache) - an in-memory cache that stores a
/// complete snapshot of a page (including the JavaScript heap) as the user is
/// navigating away.
///
/// [`pagehide`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
///
/// [`Resumed`]: Self::Resumed
Suspended, Suspended,
/// See [`ApplicationHandler::resumed`] for details. /// Emitted when the application has been resumed.
/// ///
/// [`ApplicationHandler::resumed`]: crate::application::ApplicationHandler::resumed /// For consistency, all platforms emit a `Resumed` event even if they don't themselves have a
/// formal suspend/resume lifecycle. For systems without a standard suspend/resume lifecycle
/// the `Resumed` event is always emitted after the [`NewEvents(StartCause::Init)`][StartCause::Init]
/// event.
///
/// # Portability
///
/// It's recommended that applications should only initialize their graphics context and create
/// a window after they have received their first `Resumed` event. Some systems
/// (specifically Android) won't allow applications to create a render surface until they are
/// resumed.
///
/// Considering that the implementation of [`Suspended`] and `Resumed` events may be internally
/// driven by multiple platform-specific events, and that there may be subtle differences across
/// platforms with how these internal events are delivered, it's recommended that applications
/// be able to gracefully handle redundant (i.e. back-to-back) [`Suspended`] or `Resumed` events.
///
/// Also see [`Suspended`] notes.
///
/// ## Android
///
/// On Android, the `Resumed` event is sent when a new [`SurfaceView`] has been created. This is
/// expected to closely correlate with the [`onResume`] lifecycle event but there may technically
/// be a discrepancy.
///
/// [`onResume`]: https://developer.android.com/reference/android/app/Activity#onResume()
///
/// Applications that need to run on Android must wait until they have been `Resumed`
/// before they will be able to create a render surface (such as an `EGLSurface`,
/// [`VkSurfaceKHR`] or [`wgpu::Surface`]) which depend on having a
/// [`SurfaceView`]. Applications must also assume that if they are [`Suspended`], then their
/// render surfaces are invalid and should be dropped.
///
/// Also see [`Suspended`] notes.
///
/// [`SurfaceView`]: https://developer.android.com/reference/android/view/SurfaceView
/// [Activity lifecycle]: https://developer.android.com/guide/components/activities/activity-lifecycle
/// [`VkSurfaceKHR`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSurfaceKHR.html
/// [`wgpu::Surface`]: https://docs.rs/wgpu/latest/wgpu/struct.Surface.html
///
/// ## iOS
///
/// On iOS, the `Resumed` event is emitted in response to an [`applicationDidBecomeActive`]
/// callback which means the application is "active" (according to the
/// [iOS application lifecycle]).
///
/// [`applicationDidBecomeActive`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive
/// [iOS application lifecycle]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
///
/// ## Web
///
/// On Web, the `Resumed` event is emitted in response to a [`pageshow`] event
/// with the property [`persisted`] being true, which means that the page is being
/// restored from the [`bfcache`] (back/forward cache) - an in-memory cache that
/// stores a complete snapshot of a page (including the JavaScript heap) as the
/// user is navigating away.
///
/// [`pageshow`]: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event
/// [`persisted`]: https://developer.mozilla.org/en-US/docs/Web/API/PageTransitionEvent/persisted
/// [`bfcache`]: https://web.dev/bfcache/
///
/// [`Suspended`]: Self::Suspended
Resumed, Resumed,
/// See [`ApplicationHandler::about_to_wait`] for details. /// Emitted when the event loop is about to block and wait for new events.
/// ///
/// [`ApplicationHandler::about_to_wait`]: crate::application::ApplicationHandler::about_to_wait /// Most applications shouldn't need to hook into this event since there is no real relationship
/// between how often the event loop needs to wake up and the dispatching of any specific events.
///
/// High frequency event sources, such as input devices could potentially lead to lots of wake
/// ups and also lots of corresponding `AboutToWait` events.
///
/// This is not an ideal event to drive application rendering from and instead applications
/// should render in response to [`WindowEvent::RedrawRequested`] events.
AboutToWait, AboutToWait,
/// See [`ApplicationHandler::exiting`] for details. /// Emitted when the event loop is being shut down.
/// ///
/// [`ApplicationHandler::exiting`]: crate::application::ApplicationHandler::exiting /// This is irreversible - if this event is emitted, it is guaranteed to be the last event that
/// gets emitted. You generally want to treat this as a "do on quit" event.
LoopExiting, LoopExiting,
/// See [`ApplicationHandler::memory_warning`] for details. /// Emitted when the application has received a memory warning.
/// ///
/// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning /// ## Platform-specific
///
/// ### Android
///
/// On Android, the `MemoryWarning` event is sent when [`onLowMemory`] was called. The application
/// must [release memory] or risk being killed.
///
/// [`onLowMemory`]: https://developer.android.com/reference/android/app/Application.html#onLowMemory()
/// [release memory]: https://developer.android.com/topic/performance/memory#release
///
/// ### iOS
///
/// On iOS, the `MemoryWarning` event is emitted in response to an [`applicationDidReceiveMemoryWarning`]
/// callback. The application must free as much memory as possible or risk being terminated, see
/// [how to respond to memory warnings].
///
/// [`applicationDidReceiveMemoryWarning`]: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623063-applicationdidreceivememorywarni
/// [how to respond to memory warnings]: https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings
///
/// ### Others
///
/// - **macOS / Wayland / Windows / Orbital:** Unsupported.
MemoryWarning, MemoryWarning,
} }
@@ -638,31 +781,6 @@ pub struct KeyEvent {
/// On some systems, holding down a key for some period of time causes that key to be repeated /// On some systems, holding down a key for some period of time causes that key to be repeated
/// as though it were being pressed and released repeatedly. This field is `true` if and only if /// as though it were being pressed and released repeatedly. This field is `true` if and only if
/// this event is the result of one of those repeats. /// this event is the result of one of those repeats.
///
/// # Example
///
/// In games, you often want to ignore repated key events - this can be
/// done by ignoring events where this property is set.
///
/// ```
/// use winit::event::{WindowEvent, KeyEvent, ElementState};
/// use winit::keyboard::{KeyCode, PhysicalKey};
/// # let window_event = WindowEvent::RedrawRequested; // To make the example compile
/// match window_event {
/// WindowEvent::KeyboardInput {
/// event: KeyEvent {
/// physical_key: PhysicalKey::Code(KeyCode::KeyW),
/// state: ElementState::Pressed,
/// repeat: false,
/// ..
/// },
/// ..
/// } => {
/// // The physical key `W` was pressed, and it was not a repeat
/// }
/// _ => {} // Handle other events
/// }
/// ```
pub repeat: bool, pub repeat: bool,
/// Platform-specific key event information. /// Platform-specific key event information.

View File

@@ -18,7 +18,6 @@ use std::time::{Duration, Instant};
#[cfg(web_platform)] #[cfg(web_platform)]
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, OsError}; use crate::error::{EventLoopError, OsError};
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes}; use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
use crate::{event::Event, monitor::MonitorHandle, platform_impl}; use crate::{event::Event, monitor::MonitorHandle, platform_impl};
@@ -110,8 +109,6 @@ impl<T> EventLoopBuilder<T> {
)] )]
#[inline] #[inline]
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> { pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) { if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
return Err(EventLoopError::RecreationAttempt); return Err(EventLoopError::RecreationAttempt);
} }
@@ -216,22 +213,8 @@ impl<T> EventLoop<T> {
} }
} }
/// See [`run_app`]. /// Runs the event loop in the calling thread and calls the given `event_handler` closure
/// /// to dispatch any pending events.
/// [`run_app`]: Self::run_app
#[inline]
#[deprecated = "use `EventLoop::run_app` instead"]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &ActiveEventLoop),
{
let _span = tracing::debug_span!("winit::EventLoop::run").entered();
self.event_loop.run(event_handler)
}
/// Run the application with the event loop on the calling thread.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
@@ -246,10 +229,10 @@ impl<T> EventLoop<T> {
/// Web applications are recommended to use /// Web applications are recommended to use
#[cfg_attr( #[cfg_attr(
web_platform, web_platform,
doc = "[`EventLoopExtWebSys::spawn_app()`][crate::platform::web::EventLoopExtWebSys::spawn_app()]" doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]"
)] )]
#[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")] #[cfg_attr(not(web_platform), doc = "`EventLoopExtWebSys::spawn()`")]
/// [^1] instead of [`run_app()`] to avoid the need /// [^1] instead of [`run()`] to avoid the need
/// for the Javascript exception trick, and to make it clearer that the event loop runs /// for the Javascript exception trick, and to make it clearer that the event loop runs
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the /// asynchronously (via the browser's own, internal, event loop) and doesn't block the
/// current thread of execution like it does on other platforms. /// current thread of execution like it does on other platforms.
@@ -257,13 +240,15 @@ impl<T> EventLoop<T> {
/// This function won't be available with `target_feature = "exception-handling"`. /// This function won't be available with `target_feature = "exception-handling"`.
/// ///
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
/// [`run_app()`]: Self::run_app() /// [`run()`]: Self::run()
/// [^1]: `EventLoopExtWebSys::spawn_app()` is only available on Web. /// [^1]: `EventLoopExtWebSys::spawn()` is only available on Web.
#[inline] #[inline]
#[cfg(not(all(web_platform, target_feature = "exception-handling")))] #[cfg(not(all(web_platform, target_feature = "exception-handling")))]
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> { pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
self.event_loop where
.run(|event, event_loop| dispatch_event_for_app(app, event_loop, event)) F: FnMut(Event<T>, &ActiveEventLoop),
{
self.event_loop.run(event_handler)
} }
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events /// Creates an [`EventLoopProxy`] that can be used to dispatch user events
@@ -289,12 +274,6 @@ impl<T> EventLoop<T> {
/// ///
/// [`DeviceEvent`]: crate::event::DeviceEvent /// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn listen_device_events(&self, allowed: DeviceEvents) { pub fn listen_device_events(&self, allowed: DeviceEvents) {
let _span = tracing::debug_span!(
"winit::EventLoop::listen_device_events",
allowed = ?allowed
)
.entered();
self.event_loop self.event_loop
.window_target() .window_target()
.p .p
@@ -316,12 +295,6 @@ impl<T> EventLoop<T> {
#[deprecated = "use `ActiveEventLoop::create_window` instead"] #[deprecated = "use `ActiveEventLoop::create_window` instead"]
#[inline] #[inline]
pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> { pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> {
let _span = tracing::debug_span!(
"winit::EventLoop::create_window",
window_attributes = ?window_attributes
)
.entered();
let window = let window =
platform_impl::Window::new(&self.event_loop.window_target().p, window_attributes)?; platform_impl::Window::new(&self.event_loop.window_target().p, window_attributes)?;
Ok(Window { window }) Ok(Window { window })
@@ -355,11 +328,11 @@ unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
impl<T> AsFd for EventLoop<T> { impl<T> AsFd for EventLoop<T> {
/// Get the underlying [EventLoop]'s `fd` which you can register /// Get the underlying [EventLoop]'s `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_app_events`] API. /// loop must be polled with the [`pump_events`] API.
/// ///
/// [`calloop`]: https://crates.io/crates/calloop /// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio /// [`mio`]: https://crates.io/crates/mio
/// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events /// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events
fn as_fd(&self) -> BorrowedFd<'_> { fn as_fd(&self) -> BorrowedFd<'_> {
self.event_loop.as_fd() self.event_loop.as_fd()
} }
@@ -369,11 +342,11 @@ impl<T> AsFd for EventLoop<T> {
impl<T> AsRawFd for EventLoop<T> { impl<T> AsRawFd for EventLoop<T> {
/// Get the underlying [EventLoop]'s raw `fd` which you can register /// Get the underlying [EventLoop]'s raw `fd` which you can register
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the /// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
/// loop must be polled with the [`pump_app_events`] API. /// loop must be polled with the [`pump_events`] API.
/// ///
/// [`calloop`]: https://crates.io/crates/calloop /// [`calloop`]: https://crates.io/crates/calloop
/// [`mio`]: https://crates.io/crates/mio /// [`mio`]: https://crates.io/crates/mio
/// [`pump_app_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_app_events /// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.event_loop.as_raw_fd() self.event_loop.as_raw_fd()
} }
@@ -390,28 +363,18 @@ impl ActiveEventLoop {
/// see the web platform module for more information. /// see the web platform module for more information.
#[inline] #[inline]
pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> { pub fn create_window(&self, window_attributes: WindowAttributes) -> Result<Window, OsError> {
let _span = tracing::debug_span!(
"winit::ActiveEventLoop::create_window",
window_attributes = ?window_attributes
)
.entered();
let window = platform_impl::Window::new(&self.p, window_attributes)?; let window = platform_impl::Window::new(&self.p, window_attributes)?;
Ok(Window { window }) Ok(Window { window })
} }
/// Create custom cursor. /// Create custom cursor.
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor { pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
let _span = tracing::debug_span!("winit::ActiveEventLoop::create_custom_cursor",).entered();
self.p.create_custom_cursor(custom_cursor) self.p.create_custom_cursor(custom_cursor)
} }
/// Returns the list of all the monitors available on the system. /// Returns the list of all the monitors available on the system.
#[inline] #[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
let _span = tracing::debug_span!("winit::ActiveEventLoop::available_monitors",).entered();
#[allow(clippy::useless_conversion)] // false positive on some platforms #[allow(clippy::useless_conversion)] // false positive on some platforms
self.p self.p
.available_monitors() .available_monitors()
@@ -428,8 +391,6 @@ impl ActiveEventLoop {
/// **Wayland / Web:** Always returns `None`. /// **Wayland / Web:** Always returns `None`.
#[inline] #[inline]
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {
let _span = tracing::debug_span!("winit::ActiveEventLoop::primary_monitor",).entered();
self.p self.p
.primary_monitor() .primary_monitor()
.map(|inner| MonitorHandle { inner }) .map(|inner| MonitorHandle { inner })
@@ -447,12 +408,6 @@ impl ActiveEventLoop {
/// ///
/// [`DeviceEvent`]: crate::event::DeviceEvent /// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn listen_device_events(&self, allowed: DeviceEvents) { pub fn listen_device_events(&self, allowed: DeviceEvents) {
let _span = tracing::debug_span!(
"winit::ActiveEventLoop::listen_device_events",
allowed = ?allowed
)
.entered();
self.p.listen_device_events(allowed); self.p.listen_device_events(allowed);
} }
@@ -470,8 +425,6 @@ impl ActiveEventLoop {
/// ///
/// See [`LoopExiting`](Event::LoopExiting). /// See [`LoopExiting`](Event::LoopExiting).
pub fn exit(&self) { pub fn exit(&self) {
let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered();
self.p.exit() self.p.exit()
} }
@@ -577,8 +530,6 @@ impl<T: 'static> EventLoopProxy<T> {
/// ///
/// [`UserEvent(event)`]: Event::UserEvent /// [`UserEvent(event)`]: Event::UserEvent
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
let _span = tracing::debug_span!("winit::EventLoopProxy::send_event",).entered();
self.event_loop_proxy.send_event(event) self.event_loop_proxy.send_event(event)
} }
} }
@@ -641,23 +592,3 @@ impl AsyncRequestSerial {
Self { serial } Self { serial }
} }
} }
/// Shim for various run APIs.
#[inline(always)]
pub(crate) fn dispatch_event_for_app<T: 'static, A: ApplicationHandler<T>>(
app: &mut A,
event_loop: &ActiveEventLoop,
event: Event<T>,
) {
match event {
Event::NewEvents(cause) => app.new_events(event_loop, cause),
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
Event::UserEvent(event) => app.user_event(event_loop, event),
Event::Suspended => app.suspended(event_loop),
Event::Resumed => app.resumed(event_loop),
Event::AboutToWait => app.about_to_wait(event_loop),
Event::LoopExiting => app.exiting(event_loop),
Event::MemoryWarning => app.memory_warning(event_loop),
}
}

View File

@@ -118,8 +118,6 @@ impl Icon {
/// The length of `rgba` must be divisible by 4, and `width * height` must equal /// The length of `rgba` must be divisible by 4, and `width * height` must equal
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error. /// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> { pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
let _span = tracing::debug_span!("winit::Icon::from_rgba", width, height).entered();
Ok(Icon { Ok(Icon {
inner: PlatformIcon::from_rgba(rgba, width, height)?, inner: PlatformIcon::from_rgba(rgba, width, height)?,
}) })

View File

@@ -21,7 +21,7 @@
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a //! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
//! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if desired. //! [`DeviceEvent`]. You can also create and handle your own custom [`Event::UserEvent`]s, if desired.
//! //!
//! You can retrieve events by calling [`EventLoop::run_app()`]. This function will //! You can retrieve events by calling [`EventLoop::run()`]. This function will
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and //! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`]. //! will run until [`exit()`] is used, at which point [`Event::LoopExiting`].
//! //!
@@ -36,7 +36,7 @@
x11_platform, x11_platform,
wayland_platform wayland_platform
), ),
doc = "[`EventLoopExtPumpEvents::pump_app_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_app_events()]" doc = "[`EventLoopExtPumpEvents::pump_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_events()]"
)] )]
#![cfg_attr( #![cfg_attr(
not(any( not(any(
@@ -46,54 +46,18 @@
x11_platform, x11_platform,
wayland_platform wayland_platform
)), )),
doc = "`EventLoopExtPumpEvents::pump_app_events()`" doc = "`EventLoopExtPumpEvents::pump_events()`"
)] )]
//! [^1]. See that method's documentation for more reasons about why //! [^1]. See that method's documentation for more reasons about why
//! it's discouraged beyond compatibility reasons. //! it's discouraged beyond compatibility reasons.
//! //!
//! //!
//! ```no_run //! ```no_run
//! use winit::application::ApplicationHandler; //! use winit::{
//! use winit::event::WindowEvent; //! event::{Event, WindowEvent},
//! use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop}; //! event_loop::{ControlFlow, EventLoop},
//! use winit::window::{Window, WindowId}; //! window::Window,
//! //! };
//! #[derive(Default)]
//! struct App {
//! window: Option<Window>,
//! }
//!
//! impl ApplicationHandler for App {
//! fn resumed(&mut self, event_loop: &ActiveEventLoop) {
//! self.window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
//! }
//!
//! fn window_event(&mut self, event_loop: &ActiveEventLoop, id: WindowId, event: WindowEvent) {
//! match event {
//! WindowEvent::CloseRequested => {
//! println!("The close button was pressed; stopping");
//! event_loop.exit();
//! },
//! WindowEvent::RedrawRequested => {
//! // Redraw the application.
//! //
//! // It's preferable for applications that do not render continuously to render in
//! // this event rather than in AboutToWait, since rendering in here allows
//! // the program to gracefully handle redraws requested by the OS.
//!
//! // Draw.
//!
//! // Queue a RedrawRequested event.
//! //
//! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead.
//! self.window.as_ref().unwrap().request_redraw();
//! }
//! _ => (),
//! }
//! }
//! }
//! //!
//! let event_loop = EventLoop::new().unwrap(); //! let event_loop = EventLoop::new().unwrap();
//! //!
@@ -106,8 +70,43 @@
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll. //! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
//! event_loop.set_control_flow(ControlFlow::Wait); //! event_loop.set_control_flow(ControlFlow::Wait);
//! //!
//! let mut app = App::default(); //! let mut window = None;
//! event_loop.run_app(&mut app); //!
//! event_loop.run(move |event, event_loop| {
//! match event {
//! Event::Resumed => {
//! window = Some(event_loop.create_window(Window::default_attributes()).unwrap());
//! }
//! Event::WindowEvent {
//! event: WindowEvent::CloseRequested,
//! ..
//! } => {
//! println!("The close button was pressed; stopping");
//! event_loop.exit();
//! },
//! Event::AboutToWait => {
//! // Application update code.
//!
//! // Queue a RedrawRequested event.
//! //
//! // You only need to call this if you've determined that you need to redraw in
//! // applications which do not always need to. Applications that redraw continuously
//! // can render here instead.
//! window.as_ref().unwrap().request_redraw();
//! },
//! Event::WindowEvent {
//! event: WindowEvent::RedrawRequested,
//! ..
//! } => {
//! // Redraw the application.
//! //
//! // It's preferable for applications that do not render continuously to render in
//! // this event rather than in AboutToWait, since rendering in here allows
//! // the program to gracefully handle redraws requested by the OS.
//! },
//! _ => ()
//! }
//! });
//! ``` //! ```
//! //!
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be //! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
@@ -165,7 +164,7 @@
//! //!
//! [`EventLoop`]: event_loop::EventLoop //! [`EventLoop`]: event_loop::EventLoop
//! [`EventLoop::new()`]: event_loop::EventLoop::new //! [`EventLoop::new()`]: event_loop::EventLoop::new
//! [`EventLoop::run_app()`]: event_loop::EventLoop::run_app //! [`EventLoop::run()`]: event_loop::EventLoop::run
//! [`exit()`]: event_loop::ActiveEventLoop::exit //! [`exit()`]: event_loop::ActiveEventLoop::exit
//! [`Window`]: window::Window //! [`Window`]: window::Window
//! [`WindowId`]: window::WindowId //! [`WindowId`]: window::WindowId
@@ -179,7 +178,7 @@
//! [`Event::LoopExiting`]: event::Event::LoopExiting //! [`Event::LoopExiting`]: event::Event::LoopExiting
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle //! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle //! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland. //! [^1]: `EventLoopExtPumpEvents::pump_events()` is only available on Windows, macOS, Android, X11 and Wayland.
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms)]
#![deny(rustdoc::broken_intra_doc_links)] #![deny(rustdoc::broken_intra_doc_links)]
@@ -201,7 +200,6 @@ pub use rwh_06 as raw_window_handle;
#[doc(inline)] #[doc(inline)]
pub use dpi; pub use dpi;
pub mod application;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
mod cursor; mod cursor;

View File

@@ -60,7 +60,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: //! 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` //! 1. Remove `ndk-glue` from your `Cargo.toml`
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.14", features = [ "android-native-activity" ] }` //! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.11", 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 logging as above). //! 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 logging as above).
//! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above). //! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).

View File

@@ -1,13 +1,22 @@
use std::time::Duration; use std::time::Duration;
use crate::application::ApplicationHandler; use crate::{
use crate::event::Event; event::Event,
use crate::event_loop::{self, ActiveEventLoop, EventLoop}; event_loop::{ActiveEventLoop, EventLoop},
};
/// The return status for `pump_events`
pub enum PumpStatus {
/// Continue running external loop.
Continue,
/// Exit external loop.
Exit(i32),
}
/// Additional methods on [`EventLoop`] for pumping events within an external event loop /// Additional methods on [`EventLoop`] for pumping events within an external event loop
pub trait EventLoopExtPumpEvents { pub trait EventLoopExtPumpEvents {
/// A type provided by the user that can be passed through [`Event::UserEvent`]. /// A type provided by the user that can be passed through [`Event::UserEvent`].
type UserEvent: 'static; type UserEvent;
/// Pump the `EventLoop` to check for and dispatch pending events. /// Pump the `EventLoop` to check for and dispatch pending events.
/// ///
@@ -104,21 +113,6 @@ pub trait EventLoopExtPumpEvents {
/// If you render outside of Winit you are likely to see window resizing artifacts /// If you render outside of Winit you are likely to see window resizing artifacts
/// since MacOS expects applications to render synchronously during any `drawRect` /// since MacOS expects applications to render synchronously during any `drawRect`
/// callback. /// callback.
fn pump_app_events<A: ApplicationHandler<Self::UserEvent>>(
&mut self,
timeout: Option<Duration>,
app: &mut A,
) -> PumpStatus {
#[allow(deprecated)]
self.pump_events(timeout, |event, event_loop| {
event_loop::dispatch_event_for_app(app, event_loop, event)
})
}
/// See [`pump_app_events`].
///
/// [`pump_app_events`]: Self::pump_app_events
#[deprecated = "use EventLoopExtPumpEvents::pump_app_events"]
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
where where
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop); F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
@@ -134,11 +128,3 @@ impl<T> EventLoopExtPumpEvents for EventLoop<T> {
self.event_loop.pump_events(timeout, event_handler) self.event_loop.pump_events(timeout, event_handler)
} }
} }
/// The return status for `pump_events`
pub enum PumpStatus {
/// Continue running external loop.
Continue,
/// Exit external loop.
Exit(i32),
}

View File

@@ -1,7 +1,8 @@
use crate::application::ApplicationHandler; use crate::{
use crate::error::EventLoopError; error::EventLoopError,
use crate::event::Event; event::Event,
use crate::event_loop::{self, ActiveEventLoop, EventLoop}; event_loop::{ActiveEventLoop, EventLoop},
};
#[cfg(doc)] #[cfg(doc)]
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window}; use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
@@ -9,19 +10,12 @@ use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
/// Additional methods on [`EventLoop`] to return control flow to the caller. /// Additional methods on [`EventLoop`] to return control flow to the caller.
pub trait EventLoopExtRunOnDemand { pub trait EventLoopExtRunOnDemand {
/// A type provided by the user that can be passed through [`Event::UserEvent`]. /// A type provided by the user that can be passed through [`Event::UserEvent`].
type UserEvent: 'static; type UserEvent;
/// See [`run_app_on_demand`]. /// Runs the event loop in the calling thread and calls the given `event_handler` closure
/// to dispatch any window system events.
/// ///
/// [`run_app_on_demand`]: Self::run_app_on_demand /// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures
#[deprecated = "use EventLoopExtRunOnDemand::run_app_on_demand"]
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
/// Run the application with the event loop on the calling thread.
///
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`) closures
/// and it is possible to return control back to the caller without /// and it is possible to return control back to the caller without
/// consuming the `EventLoop` (by using [`exit()`]) and /// consuming the `EventLoop` (by using [`exit()`]) and
/// so the event loop can be re-run after it has exit. /// so the event loop can be re-run after it has exit.
@@ -32,10 +26,11 @@ pub trait EventLoopExtRunOnDemand {
/// ///
/// This API is not designed to run an event loop in bursts that you can exit from and return /// This API is not designed to run an event loop in bursts that you can exit from and return
/// to while maintaining the full state of your application. (If you need something like this /// to while maintaining the full state of your application. (If you need something like this
/// you can look at the [`EventLoopExtPumpEvents::pump_app_events()`] API) /// you can look at the [`EventLoopExtPumpEvents::pump_events()`] API)
/// ///
/// Each time `run_app_on_demand` is called the startup sequence of `init`, followed by /// Each time `run_on_demand` is called the `event_handler` can expect to receive a
/// `resume` is being preserved. /// `NewEvents(Init)` and `Resumed` event (even on platforms that have no suspend/resume
/// lifecycle) - which can be used to consistently initialize application state.
/// ///
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior. /// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
/// ///
@@ -45,8 +40,8 @@ pub trait EventLoopExtRunOnDemand {
/// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead). /// backend it is possible to use `EventLoopExtWebSys::spawn()`[^1] more than once instead).
/// - No [`Window`] state can be carried between separate runs of the event loop. /// - No [`Window`] state can be carried between separate runs of the event loop.
/// ///
/// You are strongly encouraged to use [`EventLoop::run_app()`] for portability, unless you /// You are strongly encouraged to use [`EventLoop::run()`] for portability, unless you specifically need
/// specifically need the ability to re-run a single event loop more than once /// the ability to re-run a single event loop more than once
/// ///
/// # Supported Platforms /// # Supported Platforms
/// - Windows /// - Windows
@@ -69,15 +64,9 @@ pub trait EventLoopExtRunOnDemand {
/// ///
/// [`exit()`]: ActiveEventLoop::exit() /// [`exit()`]: ActiveEventLoop::exit()
/// [`set_control_flow()`]: ActiveEventLoop::set_control_flow() /// [`set_control_flow()`]: ActiveEventLoop::set_control_flow()
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>( fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
&mut self, where
app: &mut A, F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
) -> Result<(), EventLoopError> {
#[allow(deprecated)]
self.run_on_demand(|event, event_loop| {
event_loop::dispatch_event_for_app(app, event_loop, event)
})
}
} }
impl<T> EventLoopExtRunOnDemand for EventLoop<T> { impl<T> EventLoopExtRunOnDemand for EventLoop<T> {

View File

@@ -53,10 +53,9 @@ use std::time::Duration;
#[cfg(web_platform)] #[cfg(web_platform)]
use web_sys::HtmlCanvasElement; use web_sys::HtmlCanvasElement;
use crate::application::ApplicationHandler;
use crate::cursor::CustomCursorSource; use crate::cursor::CustomCursorSource;
use crate::event::Event; use crate::event::Event;
use crate::event_loop::{self, ActiveEventLoop, EventLoop}; use crate::event_loop::{ActiveEventLoop, EventLoop};
#[cfg(web_platform)] #[cfg(web_platform)]
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture; use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
use crate::platform_impl::PlatformCustomCursorSource; use crate::platform_impl::PlatformCustomCursorSource;
@@ -161,18 +160,18 @@ impl WindowAttributesExtWebSys for WindowAttributes {
/// Additional methods on `EventLoop` that are specific to the web. /// Additional methods on `EventLoop` that are specific to the web.
pub trait EventLoopExtWebSys { pub trait EventLoopExtWebSys {
/// A type provided by the user that can be passed through `Event::UserEvent`. /// A type provided by the user that can be passed through `Event::UserEvent`.
type UserEvent: 'static; type UserEvent;
/// Initializes the winit event loop. /// Initializes the winit event loop.
/// ///
/// Unlike /// Unlike
#[cfg_attr( #[cfg_attr(
all(web_platform, target_feature = "exception-handling"), all(web_platform, target_feature = "exception-handling"),
doc = "`run_app()`" doc = "`run()`"
)] )]
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]" doc = "[`run()`]"
)] )]
/// [^1], this returns immediately, and doesn't throw an exception in order to /// [^1], this returns immediately, and doesn't throw an exception in order to
/// satisfy its [`!`] return type. /// satisfy its [`!`] return type.
@@ -184,15 +183,9 @@ pub trait EventLoopExtWebSys {
/// ///
#[cfg_attr( #[cfg_attr(
not(all(web_platform, target_feature = "exception-handling")), not(all(web_platform, target_feature = "exception-handling")),
doc = "[`run_app()`]: EventLoop::run_app()" doc = "[`run()`]: EventLoop::run()"
)] )]
/// [^1]: `run_app()` is _not_ available on WASM when the target supports `exception-handling`. /// [^1]: `run()` is _not_ available on WASM when the target supports `exception-handling`.
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, app: A);
/// See [`spawn_app`].
///
/// [`spawn_app`]: Self::spawn_app
#[deprecated = "use EventLoopExtWebSys::spawn_app"]
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop); F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
@@ -201,12 +194,6 @@ pub trait EventLoopExtWebSys {
impl<T> EventLoopExtWebSys for EventLoop<T> { impl<T> EventLoopExtWebSys for EventLoop<T> {
type UserEvent = T; type UserEvent = T;
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, mut app: A) {
self.event_loop.spawn(move |event, event_loop| {
event_loop::dispatch_event_for_app(&mut app, event_loop, event)
});
}
fn spawn<F>(self, event_handler: F) fn spawn<F>(self, event_handler: F)
where where
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop), F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop),

View File

@@ -174,7 +174,7 @@ pub fn character_map_and_combine_key(
let key_map = match app.device_key_character_map(device_id) { let key_map = match app.device_key_character_map(device_id) {
Ok(key_map) => key_map, Ok(key_map) => key_map,
Err(err) => { Err(err) => {
tracing::warn!("Failed to look up `KeyCharacterMap` for device {device_id}: {err:?}"); log::warn!("Failed to look up `KeyCharacterMap` for device {device_id}: {err:?}");
return None; return None;
} }
}; };
@@ -188,7 +188,7 @@ pub fn character_map_and_combine_key(
Ok(Some(key)) => Some(key), Ok(Some(key)) => Some(key),
Ok(None) => None, Ok(None) => None,
Err(err) => { Err(err) => {
tracing::warn!("KeyEvent: Failed to combine 'dead key' accent '{accent}' with '{unicode}': {err:?}"); log::warn!("KeyEvent: Failed to combine 'dead key' accent '{accent}' with '{unicode}': {err:?}");
None None
} }
} }
@@ -213,7 +213,7 @@ pub fn character_map_and_combine_key(
None None
} }
Err(err) => { Err(err) => {
tracing::warn!("KeyEvent: Failed to get key map character: {err:?}"); log::warn!("KeyEvent: Failed to get key map character: {err:?}");
*combining_accent = None; *combining_accent = None;
None None
} }

View File

@@ -16,7 +16,7 @@ use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
use android_activity::{ use android_activity::{
AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect, AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
}; };
use tracing::{debug, trace, warn}; use log::{debug, trace, warn};
use crate::{ use crate::{
cursor::Cursor, cursor::Cursor,
@@ -330,7 +330,7 @@ impl<T: 'static> EventLoop<T> {
} }
}, },
Err(err) => { Err(err) => {
tracing::warn!("Failed to get input events iterator: {err:?}"); log::warn!("Failed to get input events iterator: {err:?}");
} }
} }
@@ -1014,7 +1014,7 @@ impl Window {
if let Some(native_window) = self.app.native_window().as_ref() { if let Some(native_window) = self.app.native_window().as_ref() {
native_window.raw_window_handle() native_window.raw_window_handle()
} else { } else {
tracing::error!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events."); log::error!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
Err(rwh_06::HandleError::Unavailable) Err(rwh_06::HandleError::Unavailable)
} }
} }

View File

@@ -622,9 +622,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
match wrapper { match wrapper {
EventWrapper::StaticEvent(event) => { EventWrapper::StaticEvent(event) => {
if !processing_redraws && event.is_redraw() { if !processing_redraws && event.is_redraw() {
tracing::info!("processing `RedrawRequested` during the main event loop"); log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !event.is_redraw() { } else if processing_redraws && !event.is_redraw() {
tracing::warn!( log::warn!(
"processing non `RedrawRequested` event after the main event loop: {:#?}", "processing non `RedrawRequested` event after the main event loop: {:#?}",
event event
); );
@@ -676,9 +676,9 @@ pub(crate) fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(
match wrapper { match wrapper {
EventWrapper::StaticEvent(event) => { EventWrapper::StaticEvent(event) => {
if !processing_redraws && event.is_redraw() { if !processing_redraws && event.is_redraw() {
tracing::info!("processing `RedrawRequested` during the main event loop"); log::info!("processing `RedrawRequested` during the main event loop");
} else if processing_redraws && !event.is_redraw() { } else if processing_redraws && !event.is_redraw() {
tracing::warn!( log::warn!(
"processing non-`RedrawRequested` event after the main event loop: {:#?}", "processing non-`RedrawRequested` event after the main event loop: {:#?}",
event event
); );
@@ -911,7 +911,7 @@ macro_rules! os_capabilities {
impl OSCapabilities {$( impl OSCapabilities {$(
$(#[$attr])* $(#[$attr])*
pub fn $error_name(&self, extra_msg: &str) { pub fn $error_name(&self, extra_msg: &str) {
tracing::warn!( log::warn!(
concat!("`", $objc_call, "` requires iOS {}.{}+. This device is running iOS {}.{}.{}. {}"), concat!("`", $objc_call, "` requires iOS {}.{}+. This device is running iOS {}.{}.{}. {}"),
$major, $minor, self.os_version.majorVersion, self.os_version.minorVersion, self.os_version.patchVersion, $major, $minor, self.os_version.majorVersion, self.os_version.minorVersion, self.os_version.patchVersion,
extra_msg extra_msg

View File

@@ -85,7 +85,7 @@ impl ActiveEventLoop {
pub(crate) fn exit(&self) { pub(crate) fn exit(&self) {
// https://developer.apple.com/library/archive/qa/qa1561/_index.html // https://developer.apple.com/library/archive/qa/qa1561/_index.html
// it is not possible to quit an iOS app gracefully and programmatically // it is not possible to quit an iOS app gracefully and programmatically
tracing::warn!("`ControlFlow::Exit` ignored on iOS"); log::warn!("`ControlFlow::Exit` ignored on iOS");
} }
pub(crate) fn exiting(&self) -> bool { pub(crate) fn exiting(&self) -> bool {
@@ -182,7 +182,7 @@ impl<T: 'static> EventLoop<T> {
application.is_none(), application.is_none(),
"\ "\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\ `EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run_app` calls `UIApplicationMain` on iOS", Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
); );
let handler = map_user_event(handler, self.receiver); let handler = map_user_event(handler, self.receiver);

View File

@@ -3,10 +3,10 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker}; use icrate::Foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
use log::{debug, warn};
use objc2::rc::Id; use objc2::rc::Id;
use objc2::runtime::{AnyObject, NSObject}; use objc2::runtime::{AnyObject, NSObject};
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass}; use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass};
use tracing::{debug, warn};
use super::app_state::EventWrapper; use super::app_state::EventWrapper;
use super::uikit::{ use super::uikit::{
@@ -689,7 +689,7 @@ impl Inner {
let screen_frame = self.rect_to_screen_space(bounds); let screen_frame = self.rect_to_screen_space(bounds);
let status_bar_frame = { let status_bar_frame = {
let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect( let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect(
"`Window::get_inner_position` cannot be called before `EventLoop::run_app` on iOS", "`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS",
); );
app.statusBarFrame() app.statusBarFrame()
}; };

View File

@@ -4,10 +4,10 @@ use std::ptr::{self, NonNull};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use crate::utils::Lazy; use crate::utils::Lazy;
use log::warn;
use smol_str::SmolStr; use smol_str::SmolStr;
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
use std::os::unix::io::OwnedFd; use std::os::unix::io::OwnedFd;
use tracing::warn;
use xkbcommon_dl::{ use xkbcommon_dl::{
self as xkb, xkb_compose_status, xkb_context, xkb_context_flags, xkbcommon_compose_handle, self as xkb, xkb_compose_status, xkb_context, xkb_context_flags, xkbcommon_compose_handle,
xkbcommon_handle, XkbCommon, XkbCommonCompose, xkbcommon_handle, XkbCommon, XkbCommonCompose,
@@ -373,15 +373,10 @@ impl<'a, 'b> KeyEventResults<'a, 'b> {
fn composed_text(&mut self) -> Result<Option<SmolStr>, ()> { fn composed_text(&mut self) -> Result<Option<SmolStr>, ()> {
match self.compose { match self.compose {
ComposeStatus::Accepted(status) => match status { ComposeStatus::Accepted(xkb_compose_status::XKB_COMPOSE_COMPOSED) => {
xkb_compose_status::XKB_COMPOSE_COMPOSED => { let state = self.context.compose_state1.as_mut().unwrap();
let state = self.context.compose_state1.as_mut().unwrap(); Ok(state.get_string(self.context.scratch_buffer))
Ok(state.get_string(self.context.scratch_buffer)) }
}
xkb_compose_status::XKB_COMPOSE_COMPOSING
| xkb_compose_status::XKB_COMPOSE_CANCELLED => Ok(None),
xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()),
},
_ => Err(()), _ => Err(()),
} }
} }
@@ -456,7 +451,7 @@ fn byte_slice_to_smol_str(bytes: &[u8]) -> Option<SmolStr> {
std::str::from_utf8(bytes) std::str::from_utf8(bytes)
.map(SmolStr::new) .map(SmolStr::new)
.map_err(|e| { .map_err(|e| {
tracing::warn!( log::warn!(
"UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", "UTF-8 received from libxkbcommon ({:?}) was invalid: {e}",
bytes bytes
) )

View File

@@ -687,7 +687,7 @@ unsafe extern "C" fn x_error_callback(
// Don't log error. // Don't log error.
if !error_handled { if !error_handled {
tracing::error!("X11 error: {:#?}", error); log::error!("X11 error: {:#?}", error);
// XXX only update the error, if it wasn't handled by any of the hooks. // XXX only update the error, if it wasn't handled by any of the hooks.
*xconn.latest_error.lock().unwrap() = Some(error); *xconn.latest_error.lock().unwrap() = Some(error);
} }

View File

@@ -584,7 +584,7 @@ impl<T: 'static> EventLoop<T> {
}; };
self.event_loop.dispatch(timeout, state).map_err(|error| { self.event_loop.dispatch(timeout, state).map_err(|error| {
tracing::error!("Error dispatching event loop: {}", error); log::error!("Error dispatching event loop: {}", error);
error.into() error.into()
}) })
} }

View File

@@ -5,7 +5,7 @@ use std::time::Duration;
use calloop::timer::{TimeoutAction, Timer}; use calloop::timer::{TimeoutAction, Timer};
use calloop::{LoopHandle, RegistrationToken}; use calloop::{LoopHandle, RegistrationToken};
use tracing::warn; use log::warn;
use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard; use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
use sctk::reexports::client::protocol::wl_keyboard::{ use sctk::reexports::client::protocol::wl_keyboard::{

View File

@@ -133,7 +133,7 @@ impl WinitState {
) { ) {
Ok(c) => Some(c), Ok(c) => Some(c),
Err(e) => { Err(e) => {
tracing::warn!("Subcompositor protocol not available, ignoring CSD: {e:?}"); log::warn!("Subcompositor protocol not available, ignoring CSD: {e:?}");
None None
} }
}; };

View File

@@ -14,7 +14,7 @@ use sctk::shell::xdg::window::Window as SctkWindow;
use sctk::shell::xdg::window::WindowDecorations; use sctk::shell::xdg::window::WindowDecorations;
use sctk::shell::WaylandSurface; use sctk::shell::WaylandSurface;
use tracing::warn; use log::warn;
use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}; use crate::dpi::{LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError}; use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};

View File

@@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex, Weak};
use std::time::Duration; use std::time::Duration;
use ahash::HashSet; use ahash::HashSet;
use tracing::{info, warn}; use log::{info, warn};
use sctk::reexports::client::backend::ObjectId; use sctk::reexports::client::backend::ObjectId;
use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_seat::WlSeat;
@@ -727,7 +727,7 @@ impl WindowState {
RootCustomCursor { RootCustomCursor {
inner: PlatformCustomCursor::X(_), inner: PlatformCustomCursor::X(_),
} => { } => {
tracing::error!("passed a X11 cursor to Wayland backend"); log::error!("passed a X11 cursor to Wayland backend");
return; return;
} }
}; };
@@ -826,14 +826,9 @@ impl WindowState {
/// Set the cursor grabbing state on the top-level. /// Set the cursor grabbing state on the top-level.
pub fn set_cursor_grab(&mut self, mode: CursorGrabMode) -> Result<(), ExternalError> { pub fn set_cursor_grab(&mut self, mode: CursorGrabMode) -> Result<(), ExternalError> {
if self.cursor_grab_mode.user_grab_mode == mode { // Replace the user grabbing mode.
return Ok(());
}
self.set_cursor_grab_inner(mode)?;
// Update user grab on success.
self.cursor_grab_mode.user_grab_mode = mode; self.cursor_grab_mode.user_grab_mode = mode;
Ok(()) self.set_cursor_grab_inner(mode)
} }
/// Reload the hints for minimum and maximum sizes. /// Reload the hints for minimum and maximum sizes.

View File

@@ -33,10 +33,9 @@ use crate::platform_impl::platform::common::xkb::Context;
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest}; use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
use crate::platform_impl::platform::x11::ActiveEventLoop; use crate::platform_impl::platform::x11::ActiveEventLoop;
use crate::platform_impl::platform::ActiveEventLoop as PlatformActiveEventLoop; use crate::platform_impl::platform::ActiveEventLoop as PlatformActiveEventLoop;
use crate::platform_impl::x11::util::cookie::GenericEventCookie;
use crate::platform_impl::x11::{ use crate::platform_impl::x11::{
atoms::*, mkdid, mkwid, util, CookieResultExt, Device, DeviceId, DeviceInfo, Dnd, DndState, atoms::*, mkdid, mkwid, util, CookieResultExt, Device, DeviceId, DeviceInfo, Dnd, DndState,
ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
}; };
/// The maximum amount of X modifiers to replay. /// The maximum amount of X modifiers to replay.
@@ -185,15 +184,14 @@ impl EventProcessor {
} }
xlib::GenericEvent => { xlib::GenericEvent => {
let wt = Self::window_target(&self.target); let wt = Self::window_target(&self.target);
let xev: GenericEventCookie = let xev = match GenericEventCookie::from_event(&wt.xconn, *xev) {
match GenericEventCookie::from_event(wt.xconn.clone(), *xev) { Some(xev) if xev.cookie.extension as u8 == self.xi2ext.major_opcode => {
Some(xev) if xev.extension() == self.xi2ext.major_opcode => xev, xev.cookie
_ => return, }
}; _ => return,
};
let evtype = xev.evtype(); match xev.evtype {
match evtype {
ty @ xinput2::XI_ButtonPress | ty @ xinput2::XI_ButtonRelease => { ty @ xinput2::XI_ButtonPress | ty @ xinput2::XI_ButtonRelease => {
let state = if ty == xinput2::XI_ButtonPress { let state = if ty == xinput2::XI_ButtonPress {
ElementState::Pressed ElementState::Pressed
@@ -201,7 +199,7 @@ impl EventProcessor {
ElementState::Released ElementState::Released
}; };
let xev: &XIDeviceEvent = unsafe { xev.as_event() }; let xev: &XIDeviceEvent = unsafe { &*(xev.data as *const _) };
self.update_mods_from_xinput2_event( self.update_mods_from_xinput2_event(
&xev.mods, &xev.mods,
&xev.group, &xev.group,
@@ -211,7 +209,7 @@ impl EventProcessor {
self.xinput2_button_input(xev, state, &mut callback); self.xinput2_button_input(xev, state, &mut callback);
} }
xinput2::XI_Motion => { xinput2::XI_Motion => {
let xev: &XIDeviceEvent = unsafe { xev.as_event() }; let xev: &XIDeviceEvent = unsafe { &*(xev.data as *const _) };
self.update_mods_from_xinput2_event( self.update_mods_from_xinput2_event(
&xev.mods, &xev.mods,
&xev.group, &xev.group,
@@ -221,11 +219,11 @@ impl EventProcessor {
self.xinput2_mouse_motion(xev, &mut callback); self.xinput2_mouse_motion(xev, &mut callback);
} }
xinput2::XI_Enter => { xinput2::XI_Enter => {
let xev: &XIEnterEvent = unsafe { xev.as_event() }; let xev: &XIEnterEvent = unsafe { &*(xev.data as *const _) };
self.xinput2_mouse_enter(xev, &mut callback); self.xinput2_mouse_enter(xev, &mut callback);
} }
xinput2::XI_Leave => { xinput2::XI_Leave => {
let xev: &XILeaveEvent = unsafe { xev.as_event() }; let xev: &XILeaveEvent = unsafe { &*(xev.data as *const _) };
self.update_mods_from_xinput2_event( self.update_mods_from_xinput2_event(
&xev.mods, &xev.mods,
&xev.group, &xev.group,
@@ -235,51 +233,51 @@ impl EventProcessor {
self.xinput2_mouse_left(xev, &mut callback); self.xinput2_mouse_left(xev, &mut callback);
} }
xinput2::XI_FocusIn => { xinput2::XI_FocusIn => {
let xev: &XIFocusInEvent = unsafe { xev.as_event() }; let xev: &XIFocusInEvent = unsafe { &*(xev.data as *const _) };
self.xinput2_focused(xev, &mut callback); self.xinput2_focused(xev, &mut callback);
} }
xinput2::XI_FocusOut => { xinput2::XI_FocusOut => {
let xev: &XIFocusOutEvent = unsafe { xev.as_event() }; let xev: &XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
self.xinput2_unfocused(xev, &mut callback); self.xinput2_unfocused(xev, &mut callback);
} }
xinput2::XI_TouchBegin | xinput2::XI_TouchUpdate | xinput2::XI_TouchEnd => { xinput2::XI_TouchBegin | xinput2::XI_TouchUpdate | xinput2::XI_TouchEnd => {
let phase = match evtype { let phase = match xev.evtype {
xinput2::XI_TouchBegin => TouchPhase::Started, xinput2::XI_TouchBegin => TouchPhase::Started,
xinput2::XI_TouchUpdate => TouchPhase::Moved, xinput2::XI_TouchUpdate => TouchPhase::Moved,
xinput2::XI_TouchEnd => TouchPhase::Ended, xinput2::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!(), _ => unreachable!(),
}; };
let xev: &XIDeviceEvent = unsafe { xev.as_event() }; let xev: &XIDeviceEvent = unsafe { &*(xev.data as *const _) };
self.xinput2_touch(xev, phase, &mut callback); self.xinput2_touch(xev, phase, &mut callback);
} }
xinput2::XI_RawButtonPress | xinput2::XI_RawButtonRelease => { xinput2::XI_RawButtonPress | xinput2::XI_RawButtonRelease => {
let state = match evtype { let state = match xev.evtype {
xinput2::XI_RawButtonPress => ElementState::Pressed, xinput2::XI_RawButtonPress => ElementState::Pressed,
xinput2::XI_RawButtonRelease => ElementState::Released, xinput2::XI_RawButtonRelease => ElementState::Released,
_ => unreachable!(), _ => unreachable!(),
}; };
let xev: &XIRawEvent = unsafe { xev.as_event() }; let xev: &XIRawEvent = unsafe { &*(xev.data as *const _) };
self.xinput2_raw_button_input(xev, state, &mut callback); self.xinput2_raw_button_input(xev, state, &mut callback);
} }
xinput2::XI_RawMotion => { xinput2::XI_RawMotion => {
let xev: &XIRawEvent = unsafe { xev.as_event() }; let xev: &XIRawEvent = unsafe { &*(xev.data as *const _) };
self.xinput2_raw_mouse_motion(xev, &mut callback); self.xinput2_raw_mouse_motion(xev, &mut callback);
} }
xinput2::XI_RawKeyPress | xinput2::XI_RawKeyRelease => { xinput2::XI_RawKeyPress | xinput2::XI_RawKeyRelease => {
let state = match evtype { let state = match xev.evtype {
xinput2::XI_RawKeyPress => ElementState::Pressed, xinput2::XI_RawKeyPress => ElementState::Pressed,
xinput2::XI_RawKeyRelease => ElementState::Released, xinput2::XI_RawKeyRelease => ElementState::Released,
_ => unreachable!(), _ => unreachable!(),
}; };
let xev: &xinput2::XIRawEvent = unsafe { xev.as_event() }; let xev: &xinput2::XIRawEvent = unsafe { &*(xev.data as *const _) };
self.xinput2_raw_key_input(xev, state, &mut callback); self.xinput2_raw_key_input(xev, state, &mut callback);
} }
xinput2::XI_HierarchyChanged => { xinput2::XI_HierarchyChanged => {
let xev: &XIHierarchyEvent = unsafe { xev.as_event() }; let xev: &XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
self.xinput2_hierarchy_changed(xev, &mut callback); self.xinput2_hierarchy_changed(xev, &mut callback);
} }
_ => {} _ => {}
@@ -981,7 +979,7 @@ impl EventProcessor {
// Always update the modifiers when we're not replaying. // Always update the modifiers when we're not replaying.
if !replay { if !replay {
self.update_mods_from_core_event(window_id, xev.state as u16, &mut callback); self.udpate_mods_from_core_event(window_id, xev.state as u16, &mut callback);
} }
if keycode != 0 && !self.is_composing { if keycode != 0 && !self.is_composing {
@@ -1520,8 +1518,8 @@ impl EventProcessor {
let mask = let mask =
unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) }; unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
let mut value = xev.raw_values; let mut value = xev.raw_values;
let mut mouse_delta = util::Delta::default(); let mut mouse_delta = (0.0, 0.0);
let mut scroll_delta = util::Delta::default(); let mut scroll_delta = (0.0, 0.0);
for i in 0..xev.valuators.mask_len * 8 { for i in 0..xev.valuators.mask_len * 8 {
if !xinput2::XIMaskIsSet(mask, i) { if !xinput2::XIMaskIsSet(mask, i) {
continue; continue;
@@ -1531,10 +1529,10 @@ impl EventProcessor {
// We assume that every XInput2 device with analog axes is a pointing device emitting // We assume that every XInput2 device with analog axes is a pointing device emitting
// relative coordinates. // relative coordinates.
match i { match i {
0 => mouse_delta.set_x(x), 0 => mouse_delta.0 = x,
1 => mouse_delta.set_y(x), 1 => mouse_delta.1 = x,
2 => scroll_delta.set_x(x as f32), 2 => scroll_delta.0 = x as f32,
3 => scroll_delta.set_y(x as f32), 3 => scroll_delta.1 = x as f32,
_ => {} _ => {}
} }
@@ -1550,7 +1548,7 @@ impl EventProcessor {
value = unsafe { value.offset(1) }; value = unsafe { value.offset(1) };
} }
if let Some(mouse_delta) = mouse_delta.consume() { if mouse_delta != (0.0, 0.0) {
let event = Event::DeviceEvent { let event = Event::DeviceEvent {
device_id: did, device_id: did,
event: DeviceEvent::MouseMotion { delta: mouse_delta }, event: DeviceEvent::MouseMotion { delta: mouse_delta },
@@ -1558,7 +1556,7 @@ impl EventProcessor {
callback(&self.target, event); callback(&self.target, event);
} }
if let Some(scroll_delta) = scroll_delta.consume() { if scroll_delta != (0.0, 0.0) {
let event = Event::DeviceEvent { let event = Event::DeviceEvent {
device_id: did, device_id: did,
event: DeviceEvent::MouseWheel { event: DeviceEvent::MouseWheel {
@@ -1778,7 +1776,7 @@ impl EventProcessor {
self.send_modifiers(window_id, mods.into(), true, &mut callback) self.send_modifiers(window_id, mods.into(), true, &mut callback)
} }
pub fn update_mods_from_core_event<T: 'static, F>( pub fn udpate_mods_from_core_event<T: 'static, F>(
&mut self, &mut self,
window_id: crate::window::WindowId, window_id: crate::window::WindowId,
state: u16, state: u16,

View File

@@ -86,7 +86,7 @@ extern "C" fn preedit_draw_callback(
let chg_range = let chg_range =
call_data.chg_first as usize..(call_data.chg_first + call_data.chg_length) as usize; 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() { if chg_range.start > client_data.text.len() || chg_range.end > client_data.text.len() {
tracing::warn!( log::warn!(
"invalid chg range: buffer length={}, but chg_first={} chg_lengthg={}", "invalid chg range: buffer length={}, but chg_first={} chg_lengthg={}",
client_data.text.len(), client_data.text.len(),
call_data.chg_first, call_data.chg_first,

View File

@@ -10,9 +10,9 @@ use std::sync::{
Arc, Arc,
}; };
use log::debug;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tracing::debug;
use super::{ffi, util, XConnection, XError}; use super::{ffi, util, XConnection, XError};

View File

@@ -18,7 +18,7 @@ use calloop::generic::Generic;
use calloop::EventLoop as Loop; use calloop::EventLoop as Loop;
use calloop::{ping::Ping, Readiness}; use calloop::{ping::Ping, Readiness};
use libc::{setlocale, LC_CTYPE}; use libc::{setlocale, LC_CTYPE};
use tracing::warn; use log::warn;
use x11rb::connection::RequestConnection; use x11rb::connection::RequestConnection;
use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError}; use x11rb::errors::{ConnectError, ConnectionError, IdsExhausted, ReplyError};
@@ -66,9 +66,6 @@ const ALL_DEVICES: u16 = 0;
const ALL_MASTER_DEVICES: u16 = 1; const ALL_MASTER_DEVICES: u16 = 1;
const ICONIC_STATE: u32 = 3; const ICONIC_STATE: u32 = 3;
/// The underlying x11rb connection that we are using.
type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
type X11Source = Generic<BorrowedFd<'static>>; type X11Source = Generic<BorrowedFd<'static>>;
struct WakeSender<T> { struct WakeSender<T> {
@@ -483,7 +480,7 @@ impl<T: 'static> EventLoop<T> {
.dispatch(timeout, &mut self.state) .dispatch(timeout, &mut self.state)
.map_err(std::io::Error::from) .map_err(std::io::Error::from)
{ {
tracing::error!("Failed to poll for events: {error:?}"); log::error!("Failed to poll for events: {error:?}");
let exit_code = error.raw_os_error().unwrap_or(1); let exit_code = error.raw_os_error().unwrap_or(1);
self.set_exit_code(exit_code); self.set_exit_code(exit_code);
return; return;
@@ -567,7 +564,7 @@ impl<T: 'static> EventLoop<T> {
callback(event, &self.event_processor.target) callback(event, &self.event_processor.target)
} }
Some(Err(e)) => { Some(Err(e)) => {
tracing::error!("Failed to get activation token: {}", e); log::error!("Failed to get activation token: {}", e);
} }
None => {} None => {}
} }
@@ -988,6 +985,9 @@ impl From<xsettings::ParserError> for X11Error {
} }
} }
/// The underlying x11rb connection that we are using.
type X11rbConnection = x11rb::xcb_ffi::XCBConnection;
/// Type alias for a void cookie. /// Type alias for a void cookie.
type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>; type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>;
@@ -1003,6 +1003,34 @@ impl<'a, E: fmt::Debug> CookieResultExt for Result<VoidCookie<'a>, E> {
} }
} }
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
struct GenericEventCookie<'a> {
xconn: &'a XConnection,
cookie: ffi::XGenericEventCookie,
}
impl<'a> GenericEventCookie<'a> {
fn from_event(xconn: &XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'_>> {
unsafe {
let mut cookie: ffi::XGenericEventCookie = From::from(event);
if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
Some(GenericEventCookie { xconn, cookie })
} else {
None
}
}
}
}
impl<'a> Drop for GenericEventCookie<'a> {
fn drop(&mut self) {
unsafe {
(self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
}
}
}
fn mkwid(w: xproto::Window) -> crate::window::WindowId { fn mkwid(w: xproto::Window) -> crate::window::WindowId {
crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _)) crate::window::WindowId(crate::platform_impl::platform::WindowId(w as _))
} }

View File

@@ -1,55 +0,0 @@
use std::ffi::c_int;
use std::sync::Arc;
use x11_dl::xlib::{self, XEvent, XGenericEventCookie};
use crate::platform_impl::x11::XConnection;
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure.
/// This is a wrapper to extract the cookie from a GenericEvent XEvent and release the cookie data
/// once it has been processed
pub struct GenericEventCookie {
cookie: XGenericEventCookie,
xconn: Arc<XConnection>,
}
impl GenericEventCookie {
pub fn from_event(xconn: Arc<XConnection>, event: XEvent) -> Option<GenericEventCookie> {
unsafe {
let mut cookie: XGenericEventCookie = From::from(event);
if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == xlib::True {
Some(GenericEventCookie { cookie, xconn })
} else {
None
}
}
}
#[inline]
pub fn extension(&self) -> u8 {
self.cookie.extension as u8
}
#[inline]
pub fn evtype(&self) -> c_int {
self.cookie.evtype
}
/// Borrow inner event data as `&T`.
///
/// ## SAFETY
///
/// The caller must ensure that the event has the `T` inside of it.
#[inline]
pub unsafe fn as_event<T>(&self) -> &T {
unsafe { &*(self.cookie.data as *const _) }
}
}
impl Drop for GenericEventCookie {
fn drop(&mut self) {
unsafe {
(self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
}
}
}

View File

@@ -45,7 +45,7 @@ impl XConnection {
self.flush_requests()?; self.flush_requests()?;
Ok(true) Ok(true)
} else { } else {
tracing::error!("Could not select XKB events: The XKB extension is not initialized!"); log::error!("Could not select XKB events: The XKB extension is not initialized!");
Ok(false) Ok(false)
} }
} }

View File

@@ -8,7 +8,6 @@ use std::{
}; };
mod client_msg; mod client_msg;
pub mod cookie;
mod cursor; mod cursor;
mod geometry; mod geometry;
mod hint; mod hint;
@@ -16,15 +15,13 @@ mod icon;
mod input; mod input;
pub mod keys; pub mod keys;
pub(crate) mod memory; pub(crate) mod memory;
mod mouse;
mod randr; mod randr;
mod window_property; mod window_property;
mod wm; mod wm;
mod xmodmap; mod xmodmap;
pub use self::{ pub use self::{
cursor::*, geometry::*, hint::*, input::*, mouse::*, window_property::*, wm::*, cursor::*, geometry::*, hint::*, input::*, window_property::*, wm::*, xmodmap::ModifierKeymap,
xmodmap::ModifierKeymap,
}; };
use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError}; use super::{atoms::*, ffi, VoidCookie, X11Error, XConnection, XError};

View File

@@ -1,52 +0,0 @@
//! Utilities for handling mouse events.
/// Recorded mouse delta designed to filter out noise.
pub struct Delta<T> {
x: T,
y: T,
}
impl<T: Default> Default for Delta<T> {
fn default() -> Self {
Self {
x: Default::default(),
y: Default::default(),
}
}
}
impl<T: Default> Delta<T> {
pub(crate) fn set_x(&mut self, x: T) {
self.x = x;
}
pub(crate) fn set_y(&mut self, y: T) {
self.y = y;
}
}
macro_rules! consume {
($this:expr, $ty:ty) => {{
let this = $this;
let (x, y) = match (this.x.abs() < <$ty>::EPSILON, this.y.abs() < <$ty>::EPSILON) {
(true, true) => return None,
(false, true) => (this.x, 0.0),
(true, false) => (0.0, this.y),
(false, false) => (this.x, this.y),
};
Some((x, y))
}};
}
impl Delta<f32> {
pub(crate) fn consume(self) -> Option<(f32, f32)> {
consume!(self, f32)
}
}
impl Delta<f64> {
pub(crate) fn consume(self) -> Option<(f64, f64)> {
consume!(self, f64)
}
}

View File

@@ -4,7 +4,7 @@ use super::*;
use crate::platform_impl::platform::x11::monitor; use crate::platform_impl::platform::x11::monitor;
use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoModeHandle}; use crate::{dpi::validate_scale_factor, platform_impl::platform::x11::VideoModeHandle};
use tracing::warn; use log::warn;
use x11rb::protocol::randr::{self, ConnectionExt as _}; use x11rb::protocol::randr::{self, ConnectionExt as _};
/// Represents values of `WINIT_HIDPI_FACTOR`. /// Represents values of `WINIT_HIDPI_FACTOR`.
@@ -44,7 +44,7 @@ impl XConnection {
Ok(Some(dpi)) => return Some(dpi), Ok(Some(dpi)) => return Some(dpi),
Ok(None) => {} Ok(None) => {}
Err(err) => { Err(err) => {
tracing::warn!("failed to fetch XSettings: {err}"); log::warn!("failed to fetch XSettings: {err}");
} }
} }
} }

View File

@@ -16,7 +16,7 @@ const NUM_MODS: usize = 8;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ModifierKeymap { pub struct ModifierKeymap {
// Maps keycodes to modifiers // Maps keycodes to modifiers
modifiers: HashSet<XKeyCode>, modifers: HashSet<XKeyCode>,
} }
impl ModifierKeymap { impl ModifierKeymap {
@@ -25,7 +25,7 @@ impl ModifierKeymap {
} }
pub fn is_modifier(&self, keycode: XKeyCode) -> bool { pub fn is_modifier(&self, keycode: XKeyCode) -> bool {
self.modifiers.contains(&keycode) self.modifers.contains(&keycode)
} }
pub fn reload_from_x_connection(&mut self, xconn: &super::XConnection) { pub fn reload_from_x_connection(&mut self, xconn: &super::XConnection) {
@@ -48,9 +48,9 @@ impl ModifierKeymap {
let keys = unsafe { let keys = unsafe {
slice::from_raw_parts(keymap.modifiermap as *const _, keys_per_mod * NUM_MODS) slice::from_raw_parts(keymap.modifiermap as *const _, keys_per_mod * NUM_MODS)
}; };
self.modifiers.clear(); self.modifers.clear();
for key in keys { for key in keys {
self.modifiers.insert(*key); self.modifers.insert(*key);
} }
} }
} }

View File

@@ -7,7 +7,7 @@ use std::{
sync::{Arc, Mutex, MutexGuard}, sync::{Arc, Mutex, MutexGuard},
}; };
use tracing::{debug, info, warn}; use log::{debug, info, warn};
use x11rb::{ use x11rb::{
connection::Connection, connection::Connection,
properties::{WmHints, WmSizeHints, WmSizeHintsSpecification}, properties::{WmHints, WmSizeHints, WmSizeHintsSpecification},
@@ -1573,7 +1573,7 @@ impl UnownedWindow {
#[cfg(wayland_platform)] #[cfg(wayland_platform)]
Cursor::Custom(RootCustomCursor { Cursor::Custom(RootCustomCursor {
inner: PlatformCustomCursor::Wayland(_), inner: PlatformCustomCursor::Wayland(_),
}) => tracing::error!("passed a Wayland cursor to X11 backend"), }) => log::error!("passed a Wayland cursor to X11 backend"),
} }
} }
@@ -1857,7 +1857,7 @@ impl UnownedWindow {
) )
.expect_then_ignore_error("Failed to send client message"); .expect_then_ignore_error("Failed to send client message");
if let Err(e) = self.xconn.flush_requests() { if let Err(e) = self.xconn.flush_requests() {
tracing::error!( log::error!(
"`flush` returned an error when focusing the window. Error was: {}", "`flush` returned an error when focusing the window. Error was: {}",
e e
); );

View File

@@ -121,7 +121,7 @@ impl XConnection {
let xsettings_screen = Self::new_xsettings_screen(&xcb, default_screen); let xsettings_screen = Self::new_xsettings_screen(&xcb, default_screen);
if xsettings_screen.is_none() { if xsettings_screen.is_none() {
tracing::warn!("error setting XSETTINGS; Xft options won't reload automatically") log::warn!("error setting XSETTINGS; Xft options won't reload automatically")
} }
// Fetch atoms. // Fetch atoms.
@@ -159,7 +159,7 @@ impl XConnection {
// Get PropertyNotify events from the XSETTINGS window. // Get PropertyNotify events from the XSETTINGS window.
// TODO: The XSETTINGS window here can change. In the future, listen for DestroyNotify on this window // TODO: The XSETTINGS window here can change. In the future, listen for DestroyNotify on this window
// in order to accommodate for a changed window here. // in order to accomodate for a changed window here.
let selector_window = xcb let selector_window = xcb
.get_selection_owner(xsettings_screen) .get_selection_owner(xsettings_screen)
.ok()? .ok()?

View File

@@ -91,7 +91,7 @@ declare_class!(
self.set_is_running(true); self.set_is_running(true);
self.dispatch_init_events(); self.dispatch_init_events();
// If the application is being launched via `EventLoop::pump_app_events()` then we'll // If the application is being launched via `EventLoop::pump_events()` then we'll
// want to stop the app once it is launched (and return to the external loop) // want to stop the app once it is launched (and return to the external loop)
// //
// In this case we still want to consider Winit's `EventLoop` to be "running", // In this case we still want to consider Winit's `EventLoop` to be "running",
@@ -471,10 +471,10 @@ fn window_activation_hack(app: &NSApplication) {
// This way we preserve the user's desired initial visibility status // This way we preserve the user's desired initial visibility status
// TODO: Also filter on the type/"level" of the window, and maybe other things? // TODO: Also filter on the type/"level" of the window, and maybe other things?
if window.isVisible() { if window.isVisible() {
tracing::trace!("Activating visible window"); log::trace!("Activating visible window");
window.makeKeyAndOrderFront(None); window.makeKeyAndOrderFront(None);
} else { } else {
tracing::trace!("Skipping activating invisible window"); log::trace!("Skipping activating invisible window");
} }
}) })
} }

View File

@@ -71,7 +71,7 @@ unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Id<NSCursor>> {
let cursor: Id<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] }; let cursor: Id<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] };
Some(cursor) Some(cursor)
} else { } else {
tracing::warn!("cursor `{sel}` appears to be invalid"); log::warn!("cursor `{sel}` appears to be invalid");
None None
} }
} }

View File

@@ -36,14 +36,14 @@ pub fn get_modifierless_char(scancode: u16) -> Key {
unsafe { unsafe {
input_source = ffi::TISCopyCurrentKeyboardLayoutInputSource(); input_source = ffi::TISCopyCurrentKeyboardLayoutInputSource();
if input_source.is_null() { if input_source.is_null() {
tracing::error!("`TISCopyCurrentKeyboardLayoutInputSource` returned null ptr"); log::error!("`TISCopyCurrentKeyboardLayoutInputSource` returned null ptr");
return Key::Unidentified(NativeKey::MacOS(scancode)); return Key::Unidentified(NativeKey::MacOS(scancode));
} }
let layout_data = let layout_data =
ffi::TISGetInputSourceProperty(input_source, ffi::kTISPropertyUnicodeKeyLayoutData); ffi::TISGetInputSourceProperty(input_source, ffi::kTISPropertyUnicodeKeyLayoutData);
if layout_data.is_null() { if layout_data.is_null() {
CFRelease(input_source as *mut c_void); CFRelease(input_source as *mut c_void);
tracing::error!("`TISGetInputSourceProperty` returned null ptr"); log::error!("`TISGetInputSourceProperty` returned null ptr");
return Key::Unidentified(NativeKey::MacOS(scancode)); return Key::Unidentified(NativeKey::MacOS(scancode));
} }
layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout; layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout;
@@ -71,7 +71,7 @@ pub fn get_modifierless_char(scancode: u16) -> Key {
CFRelease(input_source as *mut c_void); CFRelease(input_source as *mut c_void);
} }
if translate_result != 0 { if translate_result != 0 {
tracing::error!( log::error!(
"`UCKeyTranslate` returned with the non-zero value: {}", "`UCKeyTranslate` returned with the non-zero value: {}",
translate_result translate_result
); );

View File

@@ -71,7 +71,7 @@ impl EventHandler {
*data = None; *data = None;
} }
Ok(None) => { Ok(None) => {
tracing::error!("tried to clear handler, but no handler was set"); log::error!("tried to clear handler, but no handler was set");
} }
Err(_) => { Err(_) => {
// Note: This is not expected to ever happen, this // Note: This is not expected to ever happen, this
@@ -125,7 +125,7 @@ impl EventHandler {
// `NSApplication`, our app delegate and this handler are all // `NSApplication`, our app delegate and this handler are all
// global state and so it's not impossible that we could get // global state and so it's not impossible that we could get
// an event after the application has exited the `EventLoop`. // an event after the application has exited the `EventLoop`.
tracing::error!("tried to run event handler, but no handler was set"); log::error!("tried to run event handler, but no handler was set");
} }
Err(_) => { Err(_) => {
// Prevent re-entrancy. // Prevent re-entrancy.

View File

@@ -1,5 +1,5 @@
use icrate::Foundation::{NSNotFound, NSRange, NSUInteger}; use icrate::Foundation::{NSNotFound, NSRange, NSUInteger};
use tracing::trace; use log::trace;
pub const EMPTY_RANGE: NSRange = NSRange { pub const EMPTY_RANGE: NSRange = NSRange {
location: NSNotFound as NSUInteger, location: NSNotFound as NSUInteger,
@@ -20,7 +20,7 @@ pub(crate) struct TraceGuard {
impl TraceGuard { impl TraceGuard {
#[inline] #[inline]
pub(crate) fn new(module_path: &'static str, called_from_fn: &'static str) -> Self { pub(crate) fn new(module_path: &'static str, called_from_fn: &'static str) -> Self {
trace!(target = module_path, "Triggered `{}`", called_from_fn); trace!(target: module_path, "Triggered `{}`", called_from_fn);
Self { Self {
module_path, module_path,
called_from_fn, called_from_fn,
@@ -31,10 +31,6 @@ impl TraceGuard {
impl Drop for TraceGuard { impl Drop for TraceGuard {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
trace!( trace!(target: self.module_path, "Completed `{}`", self.called_from_fn);
target = self.module_path,
"Completed `{}`",
self.called_from_fn
);
} }
} }

View File

@@ -338,7 +338,7 @@ declare_class!(
// Leave the Preedit self.ivars() // Leave the Preedit self.ivars()
self.ivars().ime_state.set(ImeState::Ground); self.ivars().ime_state.set(ImeState::Ground);
} else { } else {
tracing::warn!("Expected to have IME enabled when receiving unmarkText"); log::warn!("Expected to have IME enabled when receiving unmarkText");
} }
} }

View File

@@ -537,7 +537,7 @@ impl<T: 'static> EventLoop<T> {
} }
} }
other => { other => {
tracing::warn!("unhandled event: {:?}", other); log::warn!("unhandled event: {:?}", other);
} }
} }
} }

View File

@@ -301,9 +301,7 @@ impl CursorHandler {
}; };
} }
ImageState::Failed(error) => { ImageState::Failed(error) => {
tracing::error!( log::error!("trying to load custom cursor that has failed to load: {error}")
"trying to load custom cursor that has failed to load: {error}"
)
} }
ImageState::Image(_) => { ImageState::Image(_) => {
drop(state); drop(state);
@@ -415,7 +413,7 @@ impl Inner {
self.set_style(); self.set_style();
} }
ImageState::Failed(error) => { ImageState::Failed(error) => {
tracing::error!("custom cursor failed to load: {error}"); log::error!("custom cursor failed to load: {error}");
self.cursor = previous.into() self.cursor = previous.into()
} }
ImageState::Loading { .. } => unreachable!("notified without being ready"), ImageState::Loading { .. } => unreachable!("notified without being ready"),

View File

@@ -75,8 +75,8 @@ enum RunnerEnum {
Pending, Pending,
/// The `EventLoop` is being run. /// The `EventLoop` is being run.
Running(Runner), Running(Runner),
/// The `EventLoop` is exited after being started with `EventLoop::run_app`. Since /// The `EventLoop` is exited after being started with `EventLoop::run`. Since
/// `EventLoop::run_app` takes ownership of the `EventLoop`, we can be certain /// `EventLoop::run` takes ownership of the `EventLoop`, we can be certain
/// that this event loop will never be run again. /// that this event loop will never be run again.
Destroyed, Destroyed,
} }
@@ -735,7 +735,7 @@ impl Shared {
// * `self`, i.e. the item which triggered this event loop wakeup, which // * `self`, i.e. the item which triggered this event loop wakeup, which
// is usually a `wasm-bindgen` `Closure`, which will be dropped after // is usually a `wasm-bindgen` `Closure`, which will be dropped after
// returning to the JS glue code. // returning to the JS glue code.
// * The `ActiveEventLoop` leaked inside `EventLoop::run_app` due to the // * The `ActiveEventLoop` leaked inside `EventLoop::run` due to the
// JS exception thrown at the end. // JS exception thrown at the end.
// * For each undropped `Window`: // * For each undropped `Window`:
// * The `register_redraw_request` closure. // * The `register_redraw_request` closure.

View File

@@ -190,7 +190,7 @@ pub fn key_location(event: &KeyboardEvent) -> KeyLocation {
KeyboardEvent::DOM_KEY_LOCATION_NUMPAD => KeyLocation::Numpad, KeyboardEvent::DOM_KEY_LOCATION_NUMPAD => KeyLocation::Numpad,
KeyboardEvent::DOM_KEY_LOCATION_STANDARD => KeyLocation::Standard, KeyboardEvent::DOM_KEY_LOCATION_STANDARD => KeyLocation::Standard,
location => { location => {
tracing::warn!("Unexpected key location: {location}"); log::warn!("Unexpected key location: {location}");
KeyLocation::Standard KeyLocation::Standard
} }
} }

View File

@@ -1,5 +1,5 @@
use js_sys::{Array, Object}; use js_sys::{Array, Object};
use tracing::warn; use log::warn;
use wasm_bindgen::prelude::{wasm_bindgen, Closure}; use wasm_bindgen::prelude::{wasm_bindgen, Closure};
use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen::{JsCast, JsValue};
use web_sys::{ use web_sys::{

View File

@@ -117,7 +117,9 @@ impl Schedule {
let channel = MessageChannel::new().unwrap(); let channel = MessageChannel::new().unwrap();
let closure = Closure::new(f); let closure = Closure::new(f);
let port_1 = channel.port1(); let port_1 = channel.port1();
port_1.set_onmessage(Some(closure.as_ref().unchecked_ref())); port_1
.add_event_listener_with_callback("message", closure.as_ref().unchecked_ref())
.expect("Failed to set message handler");
port_1.start(); port_1.start();
let port_2 = channel.port2(); let port_2 = channel.port2();
@@ -176,7 +178,6 @@ impl Drop for Schedule {
} => { } => {
window.clear_timeout_with_handle(*handle); window.clear_timeout_with_handle(*handle);
port.close(); port.close();
port.set_onmessage(None);
} }
} }
} }

View File

@@ -18,7 +18,7 @@ use windows_sys::{
}, },
}; };
use tracing::debug; use log::debug;
use crate::platform_impl::platform::{ use crate::platform_impl::platform::{
definitions::{IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknownVtbl}, definitions::{IDataObjectVtbl, IDropTarget, IDropTargetVtbl, IUnknownVtbl},

View File

@@ -536,7 +536,7 @@ impl ActiveEventLoop {
let inner = match WinCursor::new(&source.inner.0) { let inner = match WinCursor::new(&source.inner.0) {
Ok(cursor) => cursor, Ok(cursor) => cursor,
Err(err) => { Err(err) => {
tracing::warn!("Failed to create custom cursor: {err}"); log::warn!("Failed to create custom cursor: {err}");
WinCursor::Failed WinCursor::Failed
} }
}; };

View File

@@ -45,12 +45,12 @@ impl ImeContext {
let mut boundary_before_char = 0; let mut boundary_before_char = 0;
for (attr, chr) in attrs.into_iter().zip(text.chars()) { for (attr, chr) in attrs.into_iter().zip(text.chars()) {
let char_is_targeted = let char_is_targetted =
attr as u32 == ATTR_TARGET_CONVERTED || attr as u32 == ATTR_TARGET_NOTCONVERTED; attr as u32 == ATTR_TARGET_CONVERTED || attr as u32 == ATTR_TARGET_NOTCONVERTED;
if first.is_none() && char_is_targeted { if first.is_none() && char_is_targetted {
first = Some(boundary_before_char); first = Some(boundary_before_char);
} else if first.is_some() && last.is_none() && !char_is_targeted { } else if first.is_some() && last.is_none() && !char_is_targetted {
last = Some(boundary_before_char); last = Some(boundary_before_char);
} }

View File

@@ -32,8 +32,8 @@ use windows_sys::Win32::{
}, },
}; };
use log::{trace, warn};
use smol_str::SmolStr; use smol_str::SmolStr;
use tracing::{trace, warn};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use crate::{ use crate::{

View File

@@ -236,7 +236,7 @@ impl MonitorHandle {
let monitor_info = match get_monitor_info(self.0) { let monitor_info = match get_monitor_info(self.0) {
Ok(monitor_info) => monitor_info, Ok(monitor_info) => monitor_info,
Err(error) => { Err(error) => {
tracing::warn!("Error from get_monitor_info: {error}"); log::warn!("Error from get_monitor_info: {error}");
return modes.into_iter().map(mod_map); return modes.into_iter().map(mod_map);
} }
}; };

View File

@@ -59,7 +59,7 @@ use windows_sys::Win32::{
}, },
}; };
use tracing::warn; use log::warn;
use crate::{ use crate::{
cursor::Cursor, cursor::Cursor,

View File

@@ -20,7 +20,7 @@ use serde::{Deserialize, Serialize};
/// ///
/// The window is closed when dropped. /// The window is closed when dropped.
/// ///
/// ## Threading /// # Threading
/// ///
/// This is `Send + Sync`, meaning that it can be freely used from other /// This is `Send + Sync`, meaning that it can be freely used from other
/// threads. /// threads.
@@ -30,6 +30,37 @@ use serde::{Deserialize, Serialize};
/// window from a thread other than the main, the code is scheduled to run on /// window from a thread other than the main, the code is scheduled to run on
/// the main thread, and your thread may be blocked until that completes. /// the main thread, and your thread may be blocked until that completes.
/// ///
/// # Example
///
/// ```no_run
/// use winit::{
/// event::{Event, WindowEvent},
/// event_loop::{ControlFlow, EventLoop},
/// window::Window,
/// };
///
/// let mut event_loop = EventLoop::new().unwrap();
/// event_loop.set_control_flow(ControlFlow::Wait);
/// let mut windows = Vec::new();
///
/// event_loop.run(move |event, event_loop| {
/// match event {
/// Event::Resumed => {
/// let window = event_loop.create_window(Window::default_attributes()).unwrap();
/// windows.push(window);
/// }
/// Event::WindowEvent {
/// event: WindowEvent::CloseRequested,
/// ..
/// } => {
/// windows.clear();
/// event_loop.exit();
/// }
/// _ => (),
/// }
/// });
/// ```
///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// **Web:** The [`Window`], which is represented by a `HTMLElementCanvas`, can /// **Web:** The [`Window`], which is represented by a `HTMLElementCanvas`, can
@@ -497,8 +528,6 @@ impl Window {
/// Returns an identifier unique to the window. /// Returns an identifier unique to the window.
#[inline] #[inline]
pub fn id(&self) -> WindowId { pub fn id(&self) -> WindowId {
let _span = tracing::debug_span!("winit::Window::id",).entered();
self.window.maybe_wait_on_main(|w| WindowId(w.id())) self.window.maybe_wait_on_main(|w| WindowId(w.id()))
} }
@@ -562,8 +591,6 @@ impl Window {
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
#[inline] #[inline]
pub fn scale_factor(&self) -> f64 { pub fn scale_factor(&self) -> f64 {
let _span = tracing::debug_span!("winit::Window::scale_factor",).entered();
self.window.maybe_wait_on_main(|w| w.scale_factor()) self.window.maybe_wait_on_main(|w| w.scale_factor())
} }
@@ -595,8 +622,6 @@ impl Window {
/// [`WindowEvent::RedrawRequested`]: crate::event::WindowEvent::RedrawRequested /// [`WindowEvent::RedrawRequested`]: crate::event::WindowEvent::RedrawRequested
#[inline] #[inline]
pub fn request_redraw(&self) { pub fn request_redraw(&self) {
let _span = tracing::debug_span!("winit::Window::request_redraw",).entered();
self.window.maybe_queue_on_main(|w| w.request_redraw()) self.window.maybe_queue_on_main(|w| w.request_redraw())
} }
@@ -633,8 +658,6 @@ impl Window {
/// [`WindowEvent::RedrawRequested`]: crate::event::WindowEvent::RedrawRequested /// [`WindowEvent::RedrawRequested`]: crate::event::WindowEvent::RedrawRequested
#[inline] #[inline]
pub fn pre_present_notify(&self) { pub fn pre_present_notify(&self) {
let _span = tracing::debug_span!("winit::Window::pre_present_notify",).entered();
self.window.maybe_queue_on_main(|w| w.pre_present_notify()); self.window.maybe_queue_on_main(|w| w.pre_present_notify());
} }
@@ -651,8 +674,6 @@ impl Window {
// at least, then this function should be provided through a platform specific // at least, then this function should be provided through a platform specific
// extension trait // extension trait
pub fn reset_dead_keys(&self) { pub fn reset_dead_keys(&self) {
let _span = tracing::debug_span!("winit::Window::reset_dead_keys",).entered();
self.window.maybe_queue_on_main(|w| w.reset_dead_keys()) self.window.maybe_queue_on_main(|w| w.reset_dead_keys())
} }
} }
@@ -675,8 +696,6 @@ impl Window {
/// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc /// [safe area]: https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
#[inline] #[inline]
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> { pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let _span = tracing::debug_span!("winit::Window::inner_position",).entered();
self.window.maybe_wait_on_main(|w| w.inner_position()) self.window.maybe_wait_on_main(|w| w.inner_position())
} }
@@ -698,8 +717,6 @@ impl Window {
/// - **Android / Wayland:** Always returns [`NotSupportedError`]. /// - **Android / Wayland:** Always returns [`NotSupportedError`].
#[inline] #[inline]
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> { pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let _span = tracing::debug_span!("winit::Window::outer_position",).entered();
self.window.maybe_wait_on_main(|w| w.outer_position()) self.window.maybe_wait_on_main(|w| w.outer_position())
} }
@@ -732,12 +749,6 @@ impl Window {
#[inline] #[inline]
pub fn set_outer_position<P: Into<Position>>(&self, position: P) { pub fn set_outer_position<P: Into<Position>>(&self, position: P) {
let position = position.into(); let position = position.into();
let _span = tracing::debug_span!(
"winit::Window::set_outer_position",
position = ?position
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_outer_position(position)) .maybe_queue_on_main(move |w| w.set_outer_position(position))
} }
@@ -756,8 +767,6 @@ impl Window {
/// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform /// [`transform`]: https://developer.mozilla.org/en-US/docs/Web/CSS/transform
#[inline] #[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> { pub fn inner_size(&self) -> PhysicalSize<u32> {
let _span = tracing::debug_span!("winit::Window::inner_size",).entered();
self.window.maybe_wait_on_main(|w| w.inner_size()) self.window.maybe_wait_on_main(|w| w.inner_size())
} }
@@ -799,11 +808,6 @@ impl Window {
#[must_use] #[must_use]
pub fn request_inner_size<S: Into<Size>>(&self, size: S) -> Option<PhysicalSize<u32>> { pub fn request_inner_size<S: Into<Size>>(&self, size: S) -> Option<PhysicalSize<u32>> {
let size = size.into(); let size = size.into();
let _span = tracing::debug_span!(
"winit::Window::request_inner_size",
size = ?size
)
.entered();
self.window self.window
.maybe_wait_on_main(|w| w.request_inner_size(size)) .maybe_wait_on_main(|w| w.request_inner_size(size))
} }
@@ -821,7 +825,6 @@ impl Window {
/// [`Window::inner_size`]._ /// [`Window::inner_size`]._
#[inline] #[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> { pub fn outer_size(&self) -> PhysicalSize<u32> {
let _span = tracing::debug_span!("winit::Window::outer_size",).entered();
self.window.maybe_wait_on_main(|w| w.outer_size()) self.window.maybe_wait_on_main(|w| w.outer_size())
} }
@@ -845,11 +848,6 @@ impl Window {
#[inline] #[inline]
pub fn set_min_inner_size<S: Into<Size>>(&self, min_size: Option<S>) { pub fn set_min_inner_size<S: Into<Size>>(&self, min_size: Option<S>) {
let min_size = min_size.map(|s| s.into()); let min_size = min_size.map(|s| s.into());
let _span = tracing::debug_span!(
"winit::Window::set_min_inner_size",
min_size = ?min_size
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_min_inner_size(min_size)) .maybe_queue_on_main(move |w| w.set_min_inner_size(min_size))
} }
@@ -874,11 +872,6 @@ impl Window {
#[inline] #[inline]
pub fn set_max_inner_size<S: Into<Size>>(&self, max_size: Option<S>) { pub fn set_max_inner_size<S: Into<Size>>(&self, max_size: Option<S>) {
let max_size = max_size.map(|s| s.into()); let max_size = max_size.map(|s| s.into());
let _span = tracing::debug_span!(
"winit::Window::max_size",
max_size = ?max_size
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_max_inner_size(max_size)) .maybe_queue_on_main(move |w| w.set_max_inner_size(max_size))
} }
@@ -890,7 +883,6 @@ impl Window {
/// - **iOS / Android / Web / Wayland / Windows / Orbital:** Always returns [`None`]. /// - **iOS / Android / Web / Wayland / Windows / Orbital:** Always returns [`None`].
#[inline] #[inline]
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> { pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
let _span = tracing::debug_span!("winit::Window::resize_increments",).entered();
self.window.maybe_wait_on_main(|w| w.resize_increments()) self.window.maybe_wait_on_main(|w| w.resize_increments())
} }
@@ -907,11 +899,6 @@ impl Window {
#[inline] #[inline]
pub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>) { pub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>) {
let increments = increments.map(Into::into); let increments = increments.map(Into::into);
let _span = tracing::debug_span!(
"winit::Window::set_resize_increments",
increments = ?increments
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_resize_increments(increments)) .maybe_queue_on_main(move |w| w.set_resize_increments(increments))
} }
@@ -926,7 +913,6 @@ impl Window {
/// - **iOS / Android:** Unsupported. /// - **iOS / Android:** Unsupported.
#[inline] #[inline]
pub fn set_title(&self, title: &str) { pub fn set_title(&self, title: &str) {
let _span = tracing::debug_span!("winit::Window::set_title", title).entered();
self.window.maybe_wait_on_main(|w| w.set_title(title)) self.window.maybe_wait_on_main(|w| w.set_title(title))
} }
@@ -945,7 +931,6 @@ impl Window {
/// - **X11:** Can only be set while building the window, with [`WindowAttributes::with_transparent`]. /// - **X11:** Can only be set while building the window, with [`WindowAttributes::with_transparent`].
#[inline] #[inline]
pub fn set_transparent(&self, transparent: bool) { pub fn set_transparent(&self, transparent: bool) {
let _span = tracing::debug_span!("winit::Window::set_transparent", transparent).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_transparent(transparent)) .maybe_queue_on_main(move |w| w.set_transparent(transparent))
} }
@@ -960,7 +945,6 @@ impl Window {
/// - **Wayland:** Only works with org_kde_kwin_blur_manager protocol. /// - **Wayland:** Only works with org_kde_kwin_blur_manager protocol.
#[inline] #[inline]
pub fn set_blur(&self, blur: bool) { pub fn set_blur(&self, blur: bool) {
let _span = tracing::debug_span!("winit::Window::set_blur", blur).entered();
self.window.maybe_queue_on_main(move |w| w.set_blur(blur)) self.window.maybe_queue_on_main(move |w| w.set_blur(blur))
} }
@@ -974,7 +958,6 @@ impl Window {
/// - **iOS:** Can only be called on the main thread. /// - **iOS:** Can only be called on the main thread.
#[inline] #[inline]
pub fn set_visible(&self, visible: bool) { pub fn set_visible(&self, visible: bool) {
let _span = tracing::debug_span!("winit::Window::set_visible", visible).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_visible(visible)) .maybe_queue_on_main(move |w| w.set_visible(visible))
} }
@@ -989,7 +972,6 @@ impl Window {
/// - **Wayland / iOS / Android / Web:** Unsupported. /// - **Wayland / iOS / Android / Web:** Unsupported.
#[inline] #[inline]
pub fn is_visible(&self) -> Option<bool> { pub fn is_visible(&self) -> Option<bool> {
let _span = tracing::debug_span!("winit::Window::is_visible",).entered();
self.window.maybe_wait_on_main(|w| w.is_visible()) self.window.maybe_wait_on_main(|w| w.is_visible())
} }
@@ -1009,7 +991,6 @@ impl Window {
/// [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized /// [`WindowEvent::Resized`]: crate::event::WindowEvent::Resized
#[inline] #[inline]
pub fn set_resizable(&self, resizable: bool) { pub fn set_resizable(&self, resizable: bool) {
let _span = tracing::debug_span!("winit::Window::set_resizable", resizable).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_resizable(resizable)) .maybe_queue_on_main(move |w| w.set_resizable(resizable))
} }
@@ -1022,7 +1003,6 @@ impl Window {
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web:** Unsupported.
#[inline] #[inline]
pub fn is_resizable(&self) -> bool { pub fn is_resizable(&self) -> bool {
let _span = tracing::debug_span!("winit::Window::is_resizable",).entered();
self.window.maybe_wait_on_main(|w| w.is_resizable()) self.window.maybe_wait_on_main(|w| w.is_resizable())
} }
@@ -1033,11 +1013,6 @@ impl Window {
/// - **Wayland / X11 / Orbital:** Not implemented. /// - **Wayland / X11 / Orbital:** Not implemented.
/// - **Web / iOS / Android:** Unsupported. /// - **Web / iOS / Android:** Unsupported.
pub fn set_enabled_buttons(&self, buttons: WindowButtons) { pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
let _span = tracing::debug_span!(
"winit::Window::set_enabled_buttons",
buttons = ?buttons
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_enabled_buttons(buttons)) .maybe_queue_on_main(move |w| w.set_enabled_buttons(buttons))
} }
@@ -1049,7 +1024,6 @@ impl Window {
/// - **Wayland / X11 / Orbital:** Not implemented. Always returns [`WindowButtons::all`]. /// - **Wayland / X11 / Orbital:** Not implemented. Always returns [`WindowButtons::all`].
/// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`]. /// - **Web / iOS / Android:** Unsupported. Always returns [`WindowButtons::all`].
pub fn enabled_buttons(&self) -> WindowButtons { pub fn enabled_buttons(&self) -> WindowButtons {
let _span = tracing::debug_span!("winit::Window::enabled_buttons",).entered();
self.window.maybe_wait_on_main(|w| w.enabled_buttons()) self.window.maybe_wait_on_main(|w| w.enabled_buttons())
} }
@@ -1061,7 +1035,6 @@ impl Window {
/// - **Wayland:** Un-minimize is unsupported. /// - **Wayland:** Un-minimize is unsupported.
#[inline] #[inline]
pub fn set_minimized(&self, minimized: bool) { pub fn set_minimized(&self, minimized: bool) {
let _span = tracing::debug_span!("winit::Window::set_minimized", minimized).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_minimized(minimized)) .maybe_queue_on_main(move |w| w.set_minimized(minimized))
} }
@@ -1080,7 +1053,6 @@ impl Window {
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
#[inline] #[inline]
pub fn is_minimized(&self) -> Option<bool> { pub fn is_minimized(&self) -> Option<bool> {
let _span = tracing::debug_span!("winit::Window::is_minimized",).entered();
self.window.maybe_wait_on_main(|w| w.is_minimized()) self.window.maybe_wait_on_main(|w| w.is_minimized())
} }
@@ -1091,7 +1063,6 @@ impl Window {
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web:** Unsupported.
#[inline] #[inline]
pub fn set_maximized(&self, maximized: bool) { pub fn set_maximized(&self, maximized: bool) {
let _span = tracing::debug_span!("winit::Window::set_maximized", maximized).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_maximized(maximized)) .maybe_queue_on_main(move |w| w.set_maximized(maximized))
} }
@@ -1103,7 +1074,6 @@ impl Window {
/// - **iOS / Android / Web:** Unsupported. /// - **iOS / Android / Web:** Unsupported.
#[inline] #[inline]
pub fn is_maximized(&self) -> bool { pub fn is_maximized(&self) -> bool {
let _span = tracing::debug_span!("winit::Window::is_maximized",).entered();
self.window.maybe_wait_on_main(|w| w.is_maximized()) self.window.maybe_wait_on_main(|w| w.is_maximized())
} }
@@ -1132,11 +1102,6 @@ impl Window {
/// [transient activation]: https://developer.mozilla.org/en-US/docs/Glossary/Transient_activation /// [transient activation]: https://developer.mozilla.org/en-US/docs/Glossary/Transient_activation
#[inline] #[inline]
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) { pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
let _span = tracing::debug_span!(
"winit::Window::set_fullscreen",
fullscreen = ?fullscreen
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_fullscreen(fullscreen.map(|f| f.into()))) .maybe_queue_on_main(move |w| w.set_fullscreen(fullscreen.map(|f| f.into())))
} }
@@ -1151,7 +1116,6 @@ impl Window {
/// - **Web:** Can only return `None` or `Borderless(None)`. /// - **Web:** Can only return `None` or `Borderless(None)`.
#[inline] #[inline]
pub fn fullscreen(&self) -> Option<Fullscreen> { pub fn fullscreen(&self) -> Option<Fullscreen> {
let _span = tracing::debug_span!("winit::Window::fullscreen",).entered();
self.window self.window
.maybe_wait_on_main(|w| w.fullscreen().map(|f| f.into())) .maybe_wait_on_main(|w| w.fullscreen().map(|f| f.into()))
} }
@@ -1167,7 +1131,6 @@ impl Window {
/// - **iOS / Android / Web:** No effect. /// - **iOS / Android / Web:** No effect.
#[inline] #[inline]
pub fn set_decorations(&self, decorations: bool) { pub fn set_decorations(&self, decorations: bool) {
let _span = tracing::debug_span!("winit::Window::set_decorations", decorations).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_decorations(decorations)) .maybe_queue_on_main(move |w| w.set_decorations(decorations))
} }
@@ -1182,7 +1145,6 @@ impl Window {
/// - **iOS / Android / Web:** Always returns `true`. /// - **iOS / Android / Web:** Always returns `true`.
#[inline] #[inline]
pub fn is_decorated(&self) -> bool { pub fn is_decorated(&self) -> bool {
let _span = tracing::debug_span!("winit::Window::is_decorated",).entered();
self.window.maybe_wait_on_main(|w| w.is_decorated()) self.window.maybe_wait_on_main(|w| w.is_decorated())
} }
@@ -1192,11 +1154,6 @@ impl Window {
/// ///
/// See [`WindowLevel`] for details. /// See [`WindowLevel`] for details.
pub fn set_window_level(&self, level: WindowLevel) { pub fn set_window_level(&self, level: WindowLevel) {
let _span = tracing::debug_span!(
"winit::Window::set_window_level",
level = ?level
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_window_level(level)) .maybe_queue_on_main(move |w| w.set_window_level(level))
} }
@@ -1217,7 +1174,6 @@ impl Window {
/// said, it's usually in the same ballpark as on Windows. /// said, it's usually in the same ballpark as on Windows.
#[inline] #[inline]
pub fn set_window_icon(&self, window_icon: Option<Icon>) { pub fn set_window_icon(&self, window_icon: Option<Icon>) {
let _span = tracing::debug_span!("winit::Window::set_window_icon",).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_window_icon(window_icon)) .maybe_queue_on_main(move |w| w.set_window_icon(window_icon))
} }
@@ -1260,12 +1216,6 @@ impl Window {
pub fn set_ime_cursor_area<P: Into<Position>, S: Into<Size>>(&self, position: P, size: S) { pub fn set_ime_cursor_area<P: Into<Position>, S: Into<Size>>(&self, position: P, size: S) {
let position = position.into(); let position = position.into();
let size = size.into(); let size = size.into();
let _span = tracing::debug_span!(
"winit::Window::set_ime_cursor_area",
position = ?position,
size = ?size,
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_ime_cursor_area(position, size)) .maybe_queue_on_main(move |w| w.set_ime_cursor_area(position, size))
} }
@@ -1292,7 +1242,6 @@ impl Window {
/// [`KeyboardInput`]: crate::event::WindowEvent::KeyboardInput /// [`KeyboardInput`]: crate::event::WindowEvent::KeyboardInput
#[inline] #[inline]
pub fn set_ime_allowed(&self, allowed: bool) { pub fn set_ime_allowed(&self, allowed: bool) {
let _span = tracing::debug_span!("winit::Window::set_ime_allowed", allowed).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_ime_allowed(allowed)) .maybe_queue_on_main(move |w| w.set_ime_allowed(allowed))
} }
@@ -1304,11 +1253,6 @@ impl Window {
/// - **iOS / Android / Web / Windows / X11 / macOS / Orbital:** Unsupported. /// - **iOS / Android / Web / Windows / X11 / macOS / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_ime_purpose(&self, purpose: ImePurpose) { pub fn set_ime_purpose(&self, purpose: ImePurpose) {
let _span = tracing::debug_span!(
"winit::Window::set_ime_purpose",
purpose = ?purpose
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_ime_purpose(purpose)) .maybe_queue_on_main(move |w| w.set_ime_purpose(purpose))
} }
@@ -1325,7 +1269,6 @@ impl Window {
/// - **iOS / Android / Wayland / Orbital:** Unsupported. /// - **iOS / Android / Wayland / Orbital:** Unsupported.
#[inline] #[inline]
pub fn focus_window(&self) { pub fn focus_window(&self) {
let _span = tracing::debug_span!("winit::Window::focus_window",).entered();
self.window.maybe_queue_on_main(|w| w.focus_window()) self.window.maybe_queue_on_main(|w| w.focus_window())
} }
@@ -1336,7 +1279,6 @@ impl Window {
/// [`WindowEvent::Focused`]: crate::event::WindowEvent::Focused /// [`WindowEvent::Focused`]: crate::event::WindowEvent::Focused
#[inline] #[inline]
pub fn has_focus(&self) -> bool { pub fn has_focus(&self) -> bool {
let _span = tracing::debug_span!("winit::Window::has_focus",).entered();
self.window.maybe_wait_on_main(|w| w.has_focus()) self.window.maybe_wait_on_main(|w| w.has_focus())
} }
@@ -1355,11 +1297,6 @@ impl Window {
/// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect. /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect.
#[inline] #[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) { pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let _span = tracing::debug_span!(
"winit::Window::request_user_attention",
request_type = ?request_type
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.request_user_attention(request_type)) .maybe_queue_on_main(move |w| w.request_user_attention(request_type))
} }
@@ -1375,11 +1312,6 @@ impl Window {
/// - **iOS / Android / Web / Orbital:** Unsupported. /// - **iOS / Android / Web / Orbital:** Unsupported.
#[inline] #[inline]
pub fn set_theme(&self, theme: Option<Theme>) { pub fn set_theme(&self, theme: Option<Theme>) {
let _span = tracing::debug_span!(
"winit::Window::set_theme",
theme = ?theme
)
.entered();
self.window.maybe_queue_on_main(move |w| w.set_theme(theme)) self.window.maybe_queue_on_main(move |w| w.set_theme(theme))
} }
@@ -1391,7 +1323,6 @@ impl Window {
/// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported. /// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported.
#[inline] #[inline]
pub fn theme(&self) -> Option<Theme> { pub fn theme(&self) -> Option<Theme> {
let _span = tracing::debug_span!("winit::Window::theme",).entered();
self.window.maybe_wait_on_main(|w| w.theme()) self.window.maybe_wait_on_main(|w| w.theme())
} }
@@ -1405,8 +1336,6 @@ impl Window {
/// ///
/// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone /// [`NSWindowSharingNone`]: https://developer.apple.com/documentation/appkit/nswindowsharingtype/nswindowsharingnone
pub fn set_content_protected(&self, protected: bool) { pub fn set_content_protected(&self, protected: bool) {
let _span =
tracing::debug_span!("winit::Window::set_content_protected", protected).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_content_protected(protected)) .maybe_queue_on_main(move |w| w.set_content_protected(protected))
} }
@@ -1418,7 +1347,6 @@ impl Window {
/// - **iOS / Android / x11 / Wayland / Web:** Unsupported. Always returns an empty string. /// - **iOS / Android / x11 / Wayland / Web:** Unsupported. Always returns an empty string.
#[inline] #[inline]
pub fn title(&self) -> String { pub fn title(&self) -> String {
let _span = tracing::debug_span!("winit::Window::title",).entered();
self.window.maybe_wait_on_main(|w| w.title()) self.window.maybe_wait_on_main(|w| w.title())
} }
} }
@@ -1435,7 +1363,6 @@ impl Window {
#[inline] #[inline]
pub fn set_cursor(&self, cursor: impl Into<Cursor>) { pub fn set_cursor(&self, cursor: impl Into<Cursor>) {
let cursor = cursor.into(); let cursor = cursor.into();
let _span = tracing::debug_span!("winit::Window::set_cursor",).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_cursor(cursor)) .maybe_queue_on_main(move |w| w.set_cursor(cursor))
} }
@@ -1468,11 +1395,6 @@ impl Window {
#[inline] #[inline]
pub fn set_cursor_position<P: Into<Position>>(&self, position: P) -> Result<(), ExternalError> { pub fn set_cursor_position<P: Into<Position>>(&self, position: P) -> Result<(), ExternalError> {
let position = position.into(); let position = position.into();
let _span = tracing::debug_span!(
"winit::Window::set_cursor_position",
position = ?position
)
.entered();
self.window self.window
.maybe_wait_on_main(|w| w.set_cursor_position(position)) .maybe_wait_on_main(|w| w.set_cursor_position(position))
} }
@@ -1493,11 +1415,6 @@ impl Window {
/// ``` /// ```
#[inline] #[inline]
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> { pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
let _span = tracing::debug_span!(
"winit::Window::set_cursor_grab",
mode = ?mode
)
.entered();
self.window.maybe_wait_on_main(|w| w.set_cursor_grab(mode)) self.window.maybe_wait_on_main(|w| w.set_cursor_grab(mode))
} }
@@ -1515,7 +1432,6 @@ impl Window {
/// - **iOS / Android:** Unsupported. /// - **iOS / Android:** Unsupported.
#[inline] #[inline]
pub fn set_cursor_visible(&self, visible: bool) { pub fn set_cursor_visible(&self, visible: bool) {
let _span = tracing::debug_span!("winit::Window::set_cursor_visible", visible).entered();
self.window self.window
.maybe_queue_on_main(move |w| w.set_cursor_visible(visible)) .maybe_queue_on_main(move |w| w.set_cursor_visible(visible))
} }
@@ -1533,7 +1449,6 @@ impl Window {
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
#[inline] #[inline]
pub fn drag_window(&self) -> Result<(), ExternalError> { pub fn drag_window(&self) -> Result<(), ExternalError> {
let _span = tracing::debug_span!("winit::Window::drag_window",).entered();
self.window.maybe_wait_on_main(|w| w.drag_window()) self.window.maybe_wait_on_main(|w| w.drag_window())
} }
@@ -1548,11 +1463,6 @@ impl Window {
/// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web:** Always returns an [`ExternalError::NotSupported`].
#[inline] #[inline]
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> { pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
let _span = tracing::debug_span!(
"winit::Window::drag_resize_window",
direction = ?direction
)
.entered();
self.window self.window
.maybe_wait_on_main(|w| w.drag_resize_window(direction)) .maybe_wait_on_main(|w| w.drag_resize_window(direction))
} }
@@ -1568,11 +1478,6 @@ impl Window {
/// [window menu]: https://en.wikipedia.org/wiki/Common_menus_in_Microsoft_Windows#System_menu /// [window menu]: https://en.wikipedia.org/wiki/Common_menus_in_Microsoft_Windows#System_menu
pub fn show_window_menu(&self, position: impl Into<Position>) { pub fn show_window_menu(&self, position: impl Into<Position>) {
let position = position.into(); let position = position.into();
let _span = tracing::debug_span!(
"winit::Window::show_window_menu",
position = ?position
)
.entered();
self.window self.window
.maybe_queue_on_main(move |w| w.show_window_menu(position)) .maybe_queue_on_main(move |w| w.show_window_menu(position))
} }
@@ -1587,7 +1492,6 @@ impl Window {
/// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`]. /// - **iOS / Android / Web / Orbital:** Always returns an [`ExternalError::NotSupported`].
#[inline] #[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> { pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
let _span = tracing::debug_span!("winit::Window::set_cursor_hittest", hittest).entered();
self.window self.window
.maybe_wait_on_main(|w| w.set_cursor_hittest(hittest)) .maybe_wait_on_main(|w| w.set_cursor_hittest(hittest))
} }
@@ -1600,7 +1504,6 @@ impl Window {
/// Returns `None` if current monitor can't be detected. /// Returns `None` if current monitor can't be detected.
#[inline] #[inline]
pub fn current_monitor(&self) -> Option<MonitorHandle> { pub fn current_monitor(&self) -> Option<MonitorHandle> {
let _span = tracing::debug_span!("winit::Window::current_monitor",).entered();
self.window self.window
.maybe_wait_on_main(|w| w.current_monitor().map(|inner| MonitorHandle { inner })) .maybe_wait_on_main(|w| w.current_monitor().map(|inner| MonitorHandle { inner }))
} }
@@ -1612,7 +1515,6 @@ impl Window {
/// [`ActiveEventLoop::available_monitors`]: crate::event_loop::ActiveEventLoop::available_monitors /// [`ActiveEventLoop::available_monitors`]: crate::event_loop::ActiveEventLoop::available_monitors
#[inline] #[inline]
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> { pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
let _span = tracing::debug_span!("winit::Window::available_monitors",).entered();
self.window.maybe_wait_on_main(|w| { self.window.maybe_wait_on_main(|w| {
w.available_monitors() w.available_monitors()
.into_iter() .into_iter()
@@ -1633,7 +1535,6 @@ impl Window {
/// [`ActiveEventLoop::primary_monitor`]: crate::event_loop::ActiveEventLoop::primary_monitor /// [`ActiveEventLoop::primary_monitor`]: crate::event_loop::ActiveEventLoop::primary_monitor
#[inline] #[inline]
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {
let _span = tracing::debug_span!("winit::Window::primary_monitor",).entered();
self.window self.window
.maybe_wait_on_main(|w| w.primary_monitor().map(|inner| MonitorHandle { inner })) .maybe_wait_on_main(|w| w.primary_monitor().map(|inner| MonitorHandle { inner }))
} }

View File

@@ -1,6 +0,0 @@
# documentation: https://github.com/crate-ci/typos/blob/master/docs/reference.md
[default.extend-identifiers]
ptd = "ptd" # From winwows_sys::Win32::System::Com::FORMATETC { ptd, ..}
requestor = "requestor" # From x11_dl::xlib::XSelectionEvent { requestor ..}
XF86_Calculater = "XF86_Calculater" # From xkbcommon_dl::keysyms::XF86_Calculater