mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
25 Commits
v0.20.0-al
...
v0.20.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5291c9e28 | ||
|
|
35505a3114 | ||
|
|
830d47a5f7 | ||
|
|
1a514dff38 | ||
|
|
2888d5c6cf | ||
|
|
400f75a2b3 | ||
|
|
07bdd3e218 | ||
|
|
35a11ae24f | ||
|
|
3d28283a81 | ||
|
|
aec5a9fa09 | ||
|
|
0f94f62025 | ||
|
|
a95ebc5ee6 | ||
|
|
a70ac1531e | ||
|
|
b6e8dd0d8a | ||
|
|
af80ce842d | ||
|
|
08bae037f0 | ||
|
|
cd39327ea2 | ||
|
|
9828f368d6 | ||
|
|
1ed15c7ec7 | ||
|
|
c66784995d | ||
|
|
dba21c06ed | ||
|
|
72fc6a74ec | ||
|
|
f916311744 | ||
|
|
05a1f4280c | ||
|
|
6608a0241d |
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,6 +1,7 @@
|
||||
- [ ] Tested on all platforms changed
|
||||
- [ ] Compilation warnings were addressed
|
||||
- [ ] `cargo fmt` has been run on this branch
|
||||
- [ ] `cargo doc` builds successfully
|
||||
- [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users
|
||||
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
|
||||
- [ ] Created or updated an example program if it would help users understand this functionality
|
||||
|
||||
@@ -81,6 +81,8 @@ install:
|
||||
|
||||
script:
|
||||
- cargo +stable fmt --all -- --check
|
||||
# Ensure that the documentation builds properly.
|
||||
- cargo doc --no-deps
|
||||
# Install cargo-web to build stdweb
|
||||
- if [[ $WEB = "web" ]]; then cargo install -f cargo-web; fi
|
||||
# Build without serde then with serde
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,21 @@
|
||||
# Unreleased
|
||||
|
||||
# # 0.20.0 Alpha 5 (2019-12-09)
|
||||
|
||||
- On macOS, fix application termination on `ControlFlow::Exit`
|
||||
- On Windows, fix missing `ReceivedCharacter` events when Alt is held.
|
||||
- On macOS, stop emitting private corporate characters in `ReceivedCharacter` events.
|
||||
- On X11, fix misreporting DPI factor at startup.
|
||||
- On X11, fix events not being reported when using `run_return`.
|
||||
- On X11, fix key modifiers being incorrectly reported.
|
||||
- On X11, fix window creation hanging when another window is fullscreen.
|
||||
- On Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
|
||||
- On X11, fix reporting incorrect DPI factor when waking from suspend.
|
||||
- Change `EventLoopClosed` to contain the original event.
|
||||
- Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
|
||||
indicating that the event is generated by winit.
|
||||
- On X11, generate synthetic key events for keys held when a window gains or loses focus.
|
||||
|
||||
# 0.20.0 Alpha 4 (2019-10-18)
|
||||
|
||||
- Add web support via the 'stdweb' or 'web-sys' features
|
||||
@@ -41,6 +57,7 @@
|
||||
- This is because some platforms cannot run the event loop outside the main thread. Preventing this
|
||||
reduces the potential for cross-platform compatibility gotchyas.
|
||||
- On Windows and Linux X11/Wayland, add platform-specific functions for creating an `EventLoop` outside the main thread.
|
||||
- On Wayland, drop resize events identical to the current window size.
|
||||
|
||||
# 0.20.0 Alpha 3 (2019-08-14)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.20.0-alpha4"
|
||||
version = "0.20.0-alpha5"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2018"
|
||||
@@ -83,7 +83,7 @@ x11-dl = "2.18.3"
|
||||
percent-encoding = "2.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies.parking_lot]
|
||||
version = "0.9"
|
||||
version = "0.10"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.web_sys]
|
||||
package = "web-sys"
|
||||
|
||||
@@ -32,3 +32,4 @@ build: false
|
||||
test_script:
|
||||
- cargo test --verbose
|
||||
- cargo test --features serde --verbose
|
||||
- cargo doc --no-deps
|
||||
|
||||
121
examples/window_debug.rs
Normal file
121
examples/window_debug.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
// This example is used by developers to test various window functions.
|
||||
|
||||
use winit::{
|
||||
dpi::{LogicalSize, PhysicalSize},
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(LogicalSize::from((100, 100)))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
eprintln!("debugging keys:");
|
||||
eprintln!(" (E) Enter exclusive fullscreen");
|
||||
eprintln!(" (F) Toggle borderless fullscreen");
|
||||
#[cfg(waiting_for_set_minimized)]
|
||||
eprintln!(" (M) Toggle minimized");
|
||||
eprintln!(" (Q) Quit event loop");
|
||||
eprintln!(" (V) Toggle visibility");
|
||||
eprintln!(" (X) Toggle maximized");
|
||||
|
||||
#[cfg(waiting_for_set_minimized)]
|
||||
let mut minimized = false;
|
||||
let mut maximized = false;
|
||||
let mut visible = true;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::DeviceEvent {
|
||||
event:
|
||||
DeviceEvent::Key(KeyboardInput {
|
||||
virtual_keycode: Some(key),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
}),
|
||||
..
|
||||
} => match key {
|
||||
#[cfg(waiting_for_set_minimized)]
|
||||
VirtualKeyCode::M => {
|
||||
if minimized {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
}
|
||||
VirtualKeyCode::V => {
|
||||
if !visible {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::KeyboardInput { input, .. },
|
||||
..
|
||||
} => match input {
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(key),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => match key {
|
||||
VirtualKeyCode::E => {
|
||||
fn area(size: PhysicalSize) -> f64 {
|
||||
size.width * size.height
|
||||
}
|
||||
|
||||
let monitor = window.current_monitor();
|
||||
if let Some(mode) = monitor.video_modes().max_by(|a, b| {
|
||||
area(a.size())
|
||||
.partial_cmp(&area(b.size()))
|
||||
.expect("NaN in video mode size")
|
||||
}) {
|
||||
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
|
||||
} else {
|
||||
eprintln!("no video modes available");
|
||||
}
|
||||
}
|
||||
VirtualKeyCode::F => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
let monitor = window.current_monitor();
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
|
||||
}
|
||||
}
|
||||
#[cfg(waiting_for_set_minimized)]
|
||||
VirtualKeyCode::M => {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
VirtualKeyCode::Q => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
VirtualKeyCode::V => {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
VirtualKeyCode::X => {
|
||||
maximized = !maximized;
|
||||
window.set_maximized(maximized);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
fn main() {
|
||||
use std::{thread::sleep, time::Duration};
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
@@ -17,36 +18,38 @@ fn main() {
|
||||
};
|
||||
let mut event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
let _window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!("Close the window to continue.");
|
||||
event_loop.run_return(|event, _, control_flow| match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
_ => *control_flow = ControlFlow::Wait,
|
||||
});
|
||||
drop(window);
|
||||
let mut quit = false;
|
||||
|
||||
let _window_2 = WindowBuilder::new()
|
||||
.with_title("A second, fantasticer window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
while !quit {
|
||||
event_loop.run_return(|event, _, control_flow| {
|
||||
if let Event::WindowEvent { event, .. } = &event {
|
||||
// Print only Window events to reduce noise
|
||||
println!("{:?}", event);
|
||||
}
|
||||
|
||||
println!("Wa ha ha! You thought that closing the window would finish this?!");
|
||||
event_loop.run_return(|event, _, control_flow| match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
_ => *control_flow = ControlFlow::Wait,
|
||||
});
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
quit = true;
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
Event::EventsCleared => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
_ => *control_flow = ControlFlow::Wait,
|
||||
}
|
||||
});
|
||||
|
||||
println!("Okay we're done now for real.");
|
||||
// Sleep for 1/60 second to simulate rendering
|
||||
sleep(Duration::from_millis(16));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))]
|
||||
|
||||
20
src/dpi.rs
20
src/dpi.rs
@@ -28,18 +28,18 @@
|
||||
//! them entering an existential panic. Once users enter that state, they will no longer be focused on your application.
|
||||
//!
|
||||
//! There are two ways to get the DPI factor:
|
||||
//! - You can track the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) event of your
|
||||
//! - You can track the [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged) event of your
|
||||
//! windows. This event is sent any time the DPI factor changes, either because the window moved to another monitor,
|
||||
//! or because the user changed the configuration of their screen.
|
||||
//! - You can also retrieve the DPI factor of a monitor by calling
|
||||
//! [`MonitorHandle::hidpi_factor`](../monitor/struct.MonitorHandle.html#method.hidpi_factor), or the
|
||||
//! [`MonitorHandle::hidpi_factor`](crate::monitor::MonitorHandle::hidpi_factor), or the
|
||||
//! current DPI factor applied to a window by calling
|
||||
//! [`Window::hidpi_factor`](../window/struct.Window.html#method.hidpi_factor), which is roughly equivalent
|
||||
//! [`Window::hidpi_factor`](crate::window::Window::hidpi_factor), which is roughly equivalent
|
||||
//! to `window.current_monitor().hidpi_factor()`.
|
||||
//!
|
||||
//! Depending on the platform, the window's actual DPI factor may only be known after
|
||||
//! the event loop has started and your window has been drawn once. To properly handle these cases,
|
||||
//! the most robust way is to monitor the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged)
|
||||
//! the most robust way is to monitor the [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged)
|
||||
//! event and dynamically adapt your drawing logic to follow the DPI factor.
|
||||
//!
|
||||
//! Here's an overview of what sort of DPI factors you can expect, and where they come from:
|
||||
@@ -49,7 +49,7 @@
|
||||
//! - **macOS:** The buzzword is "retina displays", which have a DPI factor of 2.0. Otherwise, the DPI factor is 1.0.
|
||||
//! Intermediate DPI factors are never used, thus 1440p displays/etc. aren't properly supported. It's possible for any
|
||||
//! display to use that 2.0 DPI factor, given the use of the command line.
|
||||
//! - **X11:** On X11, we calcuate the DPI factor based on the millimeter dimensions provided by XRandR. This can
|
||||
//! - **X11:** On X11, we calculate the DPI factor based on the millimeter dimensions provided by XRandR. This can
|
||||
//! result in a wide range of possible values, including some interesting ones like 1.0833333333333333. This can be
|
||||
//! overridden using the `WINIT_HIDPI_FACTOR` environment variable, though that's not recommended.
|
||||
//! - **Wayland:** On Wayland, DPI factors are set per-screen by the server, and are always integers (most often 1 or 2).
|
||||
@@ -59,21 +59,21 @@
|
||||
//!
|
||||
//! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This
|
||||
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes always produce a
|
||||
//! [`Resized`](../event/enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs,
|
||||
//! [`Resized`](crate::event::WindowEvent::Resized) event, even on platforms where no resize actually occurs,
|
||||
//! such as macOS and Wayland. As a result, it's not necessary to separately handle
|
||||
//! [`HiDpiFactorChanged`](../event/enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size.
|
||||
//! [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged) if you're only listening for size.
|
||||
//!
|
||||
//! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your
|
||||
//! framebuffer's size should be in physical pixels.
|
||||
//!
|
||||
//! `winit` will send [`Resized`](../enum.WindowEvent.html#variant.Resized) events whenever a window's logical size
|
||||
//! changes, and [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) events
|
||||
//! `winit` will send [`Resized`](crate::event::WindowEvent::Resized) events whenever a window's logical size
|
||||
//! changes, and [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged) events
|
||||
//! whenever the DPI factor changes. Receiving either of these events means that the physical size of your window has
|
||||
//! changed, and you should recompute it using the latest values you received for each. If the logical size and the
|
||||
//! DPI factor change simultaneously, `winit` will send both events together; thus, it's recommended to buffer
|
||||
//! these events and process them at the end of the queue.
|
||||
//!
|
||||
//! If you never received any [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) events,
|
||||
//! If you never received any [`HiDpiFactorChanged`](crate::event::WindowEvent::HiDpiFactorChanged) events,
|
||||
//! then your window's DPI factor is 1.
|
||||
|
||||
/// Checks that the DPI factor is a normal positive `f64`.
|
||||
|
||||
27
src/event.rs
27
src/event.rs
@@ -3,7 +3,7 @@
|
||||
//! These are sent to the closure given to [`EventLoop::run(...)`][event_loop_run], where they get
|
||||
//! processed and used to modify the program state. For more details, see the root-level documentation.
|
||||
//!
|
||||
//! [event_loop_run]: ../event_loop/struct.EventLoop.html#method.run
|
||||
//! [event_loop_run]: crate::event_loop::EventLoop::run
|
||||
use instant::Instant;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -26,7 +26,7 @@ pub enum Event<T> {
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
},
|
||||
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](../event_loop/struct.EventLoopProxy.html#method.send_event)
|
||||
/// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event)
|
||||
UserEvent(T),
|
||||
/// Emitted when new events arrive from the OS to be processed.
|
||||
NewEvents(StartCause),
|
||||
@@ -132,12 +132,17 @@ pub enum WindowEvent {
|
||||
KeyboardInput {
|
||||
device_id: DeviceId,
|
||||
input: KeyboardInput,
|
||||
/// If `true`, the event was generated synthetically by winit
|
||||
/// in one of the following circumstances:
|
||||
///
|
||||
/// * **X11**: Synthetic key press events are generated for all keys pressed
|
||||
/// when a window gains focus. Likewise, synthetic key release events
|
||||
/// are generated for all keys pressed when a window goes out of focus.
|
||||
///
|
||||
/// Otherwise, this value is always `false`.
|
||||
is_synthetic: bool,
|
||||
},
|
||||
|
||||
/// Keyboard modifiers have changed
|
||||
#[doc(hidden)]
|
||||
ModifiersChanged { modifiers: ModifiersState },
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
CursorMoved {
|
||||
device_id: DeviceId,
|
||||
@@ -203,7 +208,7 @@ pub enum WindowEvent {
|
||||
/// * Changing the display's DPI factor (e.g. in Control Panel on Windows).
|
||||
/// * Moving the window to a display with a different DPI factor.
|
||||
///
|
||||
/// For more information about DPI in general, see the [`dpi`](../dpi/index.html) module.
|
||||
/// For more information about DPI in general, see the [`dpi`](crate::dpi) module.
|
||||
HiDpiFactorChanged(f64),
|
||||
}
|
||||
|
||||
@@ -266,7 +271,15 @@ pub enum DeviceEvent {
|
||||
button: ButtonId,
|
||||
state: ElementState,
|
||||
},
|
||||
|
||||
Key(KeyboardInput),
|
||||
|
||||
/// Keyboard modifiers have changed
|
||||
#[doc(hidden)]
|
||||
ModifiersChanged {
|
||||
modifiers: ModifiersState,
|
||||
},
|
||||
|
||||
Text {
|
||||
codepoint: char,
|
||||
},
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
//! See the root-level documentation for information on how to create and use an event loop to
|
||||
//! handle events.
|
||||
//!
|
||||
//! [create_proxy]: ./struct.EventLoop.html#method.create_proxy
|
||||
//! [event_loop_proxy]: ./struct.EventLoopProxy.html
|
||||
//! [send_event]: ./struct.EventLoopProxy.html#method.send_event
|
||||
//! [create_proxy]: crate::event_loop::EventLoop::create_proxy
|
||||
//! [event_loop_proxy]: crate::event_loop::EventLoopProxy
|
||||
//! [send_event]: crate::event_loop::EventLoopProxy::send_event
|
||||
use instant::Instant;
|
||||
use std::ops::Deref;
|
||||
use std::{error, fmt};
|
||||
@@ -68,7 +68,7 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
||||
/// are **not** persistent between multiple calls to `run_return` - issuing a new call will reset
|
||||
/// the control flow to `Poll`.
|
||||
///
|
||||
/// [events_cleared]: ../event/enum.Event.html#variant.EventsCleared
|
||||
/// [events_cleared]: crate::event::Event::EventsCleared
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
@@ -139,7 +139,7 @@ impl<T> EventLoop<T> {
|
||||
///
|
||||
/// Any values not passed to this function will *not* be dropped.
|
||||
///
|
||||
/// [`ControlFlow`]: ./enum.ControlFlow.html
|
||||
/// [`ControlFlow`]: crate::event_loop::ControlFlow
|
||||
#[inline]
|
||||
pub fn run<F>(self, event_handler: F) -> !
|
||||
where
|
||||
@@ -199,7 +199,7 @@ impl<T: 'static> EventLoopProxy<T> {
|
||||
/// function.
|
||||
///
|
||||
/// Returns an `Err` if the associated `EventLoop` no longer exists.
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.event_loop_proxy.send_event(event)
|
||||
}
|
||||
}
|
||||
@@ -211,17 +211,17 @@ impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
|
||||
}
|
||||
|
||||
/// The error that is returned when an `EventLoopProxy` attempts to wake up an `EventLoop` that
|
||||
/// no longer exists.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct EventLoopClosed;
|
||||
/// no longer exists. Contains the original event given to `send_event`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventLoopClosed<T>(pub T);
|
||||
|
||||
impl fmt::Display for EventLoopClosed {
|
||||
impl<T: fmt::Debug> fmt::Display for EventLoopClosed<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for EventLoopClosed {
|
||||
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {
|
||||
fn description(&self) -> &str {
|
||||
"Tried to wake up a closed `EventLoop`"
|
||||
}
|
||||
|
||||
37
src/lib.rs
37
src/lib.rs
@@ -92,25 +92,26 @@
|
||||
//! retrieve the raw handle of the window (see the [`platform`] module), which in turn allows you
|
||||
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the [`Window`].
|
||||
//!
|
||||
//! [`EventLoop`]: ./event_loop/struct.EventLoop.html
|
||||
//! [`EventLoop::new()`]: ./event_loop/struct.EventLoop.html#method.new
|
||||
//! [event_loop_run]: ./event_loop/struct.EventLoop.html#method.run
|
||||
//! [`ControlFlow`]: ./event_loop/enum.ControlFlow.html
|
||||
//! [`Exit`]: ./event_loop/enum.ControlFlow.html#variant.Exit
|
||||
//! [`Window`]: ./window/struct.Window.html
|
||||
//! [`WindowBuilder`]: ./window/struct.WindowBuilder.html
|
||||
//! [window_new]: ./window/struct.Window.html#method.new
|
||||
//! [window_builder_new]: ./window/struct.WindowBuilder.html#method.new
|
||||
//! [window_builder_build]: ./window/struct.WindowBuilder.html#method.build
|
||||
//! [window_id_fn]: ./window/struct.Window.html#method.id
|
||||
//! [`Event`]: ./event/enum.Event.html
|
||||
//! [`WindowEvent`]: ./event/enum.WindowEvent.html
|
||||
//! [`DeviceEvent`]: ./event/enum.DeviceEvent.html
|
||||
//! [`UserEvent`]: ./event/enum.Event.html#variant.UserEvent
|
||||
//! [`LoopDestroyed`]: ./event/enum.Event.html#variant.LoopDestroyed
|
||||
//! [`platform`]: ./platform/index.html
|
||||
//! [`EventLoop`]: event_loop::EventLoop
|
||||
//! [`EventLoop::new()`]: event_loop::EventLoop::new
|
||||
//! [event_loop_run]: event_loop::EventLoop::run
|
||||
//! [`ControlFlow`]: event_loop::ControlFlow
|
||||
//! [`Exit`]: event_loop::ControlFlow::Exit
|
||||
//! [`Window`]: window::Window
|
||||
//! [`WindowBuilder`]: window::WindowBuilder
|
||||
//! [window_new]: window::Window::new
|
||||
//! [window_builder_new]: window::WindowBuilder::new
|
||||
//! [window_builder_build]: window::WindowBuilder::build
|
||||
//! [window_id_fn]: window::Window::id
|
||||
//! [`Event`]: event::Event
|
||||
//! [`WindowEvent`]: event::WindowEvent
|
||||
//! [`DeviceEvent`]: event::DeviceEvent
|
||||
//! [`UserEvent`]: event::Event::UserEvent
|
||||
//! [`LoopDestroyed`]: event::Event::LoopDestroyed
|
||||
//! [`platform`]: platform
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
@@ -126,7 +127,7 @@ extern crate bitflags;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
#[cfg(feature = "std_web")]
|
||||
#[cfg(all(target_arch = "wasm32", feature = "std_web"))]
|
||||
extern crate std_web as stdweb;
|
||||
|
||||
pub mod dpi;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//! Types useful for interacting with a user's monitors.
|
||||
//!
|
||||
//! If you want to get basic information about a monitor, you can use the [`MonitorHandle`][monitor_id]
|
||||
//! If you want to get basic information about a monitor, you can use the [`MonitorHandle`][monitor_handle]
|
||||
//! type. This is retreived from one of the following methods, which return an iterator of
|
||||
//! [`MonitorHandle`][monitor_id]:
|
||||
//! [`MonitorHandle`][monitor_handle]:
|
||||
//! - [`EventLoop::available_monitors`][loop_get]
|
||||
//! - [`Window::available_monitors`][window_get].
|
||||
//!
|
||||
//! [monitor_id]: ./struct.MonitorHandle.html
|
||||
//! [loop_get]: ../event_loop/struct.EventLoop.html#method.available_monitors
|
||||
//! [window_get]: ../window/struct.Window.html#method.available_monitors
|
||||
//! [monitor_handle]: crate::monitor::MonitorHandle
|
||||
//! [loop_get]: crate::event_loop::EventLoop::available_monitors
|
||||
//! [window_get]: crate::window::Window::available_monitors
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
platform_impl,
|
||||
@@ -19,7 +19,7 @@ use crate::{
|
||||
/// Can be acquired with:
|
||||
/// - [`MonitorHandle::video_modes`][monitor_get].
|
||||
///
|
||||
/// [monitor_get]: ../monitor/struct.MonitorHandle.html#method.video_modes
|
||||
/// [monitor_get]: crate::monitor::MonitorHandle::video_modes
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VideoMode {
|
||||
pub(crate) video_mode: platform_impl::VideoMode,
|
||||
@@ -108,7 +108,7 @@ impl std::fmt::Display for VideoMode {
|
||||
///
|
||||
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
|
||||
///
|
||||
/// [`Window`]: ../window/struct.Window.html
|
||||
/// [`Window`]: crate::window::Window
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MonitorHandle {
|
||||
pub(crate) inner: platform_impl::MonitorHandle,
|
||||
@@ -150,7 +150,7 @@ impl MonitorHandle {
|
||||
|
||||
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||
///
|
||||
/// See the [`dpi`](../dpi/index.html) module for more information.
|
||||
/// See the [`dpi`](crate::dpi) module for more information.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
|
||||
@@ -157,7 +157,7 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
impl EventLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), ::EventLoopClosed> {
|
||||
pub fn wakeup(&self) -> Result<(), ::EventLoopClosed<()>> {
|
||||
android_glue::wake_event_loop();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -165,8 +165,10 @@ impl<T> EventLoopProxy<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
self.sender.send(event).map_err(|_| EventLoopClosed)?;
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.sender
|
||||
.send(event)
|
||||
.map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
|
||||
@@ -500,7 +500,9 @@ pub unsafe fn create_window(
|
||||
let () = msg_send![uiscreen, setCurrentMode: video_mode.video_mode.screen_mode];
|
||||
msg_send![window, setScreen:video_mode.monitor().ui_screen()]
|
||||
}
|
||||
Some(Fullscreen::Borderless(ref monitor)) => msg_send![window, setScreen:monitor.ui_screen()],
|
||||
Some(Fullscreen::Borderless(ref monitor)) => {
|
||||
msg_send![window, setScreen:monitor.ui_screen()]
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
|
||||
|
||||
@@ -650,7 +650,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
match *self {
|
||||
EventLoopProxy::Wayland(ref proxy) => proxy.send_event(event),
|
||||
EventLoopProxy::X(ref proxy) => proxy.send_event(event),
|
||||
|
||||
@@ -54,6 +54,10 @@ impl<T> WindowEventsSink<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_event(&mut self, evt: crate::event::Event<T>) {
|
||||
self.buffer.push_back(evt);
|
||||
}
|
||||
|
||||
pub fn send_window_event(&mut self, evt: crate::event::WindowEvent, wid: WindowId) {
|
||||
self.buffer.push_back(crate::event::Event::WindowEvent {
|
||||
event: evt,
|
||||
@@ -240,9 +244,7 @@ pub struct EventLoop<T: 'static> {
|
||||
// Utility for grabbing the cursor and changing visibility
|
||||
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
|
||||
user_sender: ::calloop::channel::Sender<T>,
|
||||
_kbd_source: ::calloop::Source<
|
||||
::calloop::channel::Channel<(crate::event::WindowEvent, super::WindowId)>,
|
||||
>,
|
||||
_kbd_source: ::calloop::Source<::calloop::channel::Channel<crate::event::Event<()>>>,
|
||||
window_target: RootELW<T>,
|
||||
}
|
||||
|
||||
@@ -280,8 +282,14 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
self.user_sender.send(event).map_err(|_| EventLoopClosed)
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.user_sender.send(event).map_err(|e| {
|
||||
EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e {
|
||||
x
|
||||
} else {
|
||||
unreachable!()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,13 +304,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
let inner_loop = ::calloop::EventLoop::new().unwrap();
|
||||
|
||||
let (kbd_sender, kbd_channel) = ::calloop::channel::channel();
|
||||
let (kbd_sender, kbd_channel) = ::calloop::channel::channel::<crate::event::Event<()>>();
|
||||
let kbd_sink = sink.clone();
|
||||
let kbd_source = inner_loop
|
||||
.handle()
|
||||
.insert_source(kbd_channel, move |evt, &mut ()| {
|
||||
if let ::calloop::channel::Event::Msg((evt, wid)) = evt {
|
||||
kbd_sink.lock().unwrap().send_window_event(evt, wid);
|
||||
if let ::calloop::channel::Event::Msg(evt) = evt {
|
||||
let evt = evt.map_nonuser_event().ok().unwrap();
|
||||
kbd_sink.lock().unwrap().send_event(evt);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
@@ -661,53 +670,53 @@ impl<T> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
// process pending resize/refresh
|
||||
window_target.store.lock().unwrap().for_each(
|
||||
|newsize,
|
||||
size,
|
||||
new_dpi,
|
||||
refresh,
|
||||
frame_refresh,
|
||||
closed,
|
||||
grab_cursor,
|
||||
surface,
|
||||
wid,
|
||||
frame| {
|
||||
if let Some(frame) = frame {
|
||||
if let Some((w, h)) = newsize {
|
||||
window_target.store.lock().unwrap().for_each(|window| {
|
||||
if let Some(frame) = window.frame {
|
||||
if let Some(newsize) = window.newsize {
|
||||
// Drop resize events equaled to the current size
|
||||
if newsize != *window.size {
|
||||
let (w, h) = newsize;
|
||||
frame.resize(w, h);
|
||||
frame.refresh();
|
||||
let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
|
||||
sink.send_window_event(
|
||||
crate::event::WindowEvent::Resized(logical_size),
|
||||
wid,
|
||||
window.wid,
|
||||
);
|
||||
*size = (w, h);
|
||||
} else if frame_refresh {
|
||||
*window.size = (w, h);
|
||||
} else {
|
||||
// Refresh csd, etc, otherwise
|
||||
frame.refresh();
|
||||
if !refresh {
|
||||
frame.surface().commit()
|
||||
}
|
||||
}
|
||||
} else if window.frame_refresh {
|
||||
frame.refresh();
|
||||
if !window.refresh {
|
||||
frame.surface().commit()
|
||||
}
|
||||
}
|
||||
if let Some(dpi) = new_dpi {
|
||||
sink.send_window_event(
|
||||
crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64),
|
||||
wid,
|
||||
);
|
||||
}
|
||||
if refresh {
|
||||
sink.send_window_event(crate::event::WindowEvent::RedrawRequested, wid);
|
||||
}
|
||||
if closed {
|
||||
sink.send_window_event(crate::event::WindowEvent::CloseRequested, wid);
|
||||
}
|
||||
}
|
||||
if let Some(dpi) = window.new_dpi {
|
||||
sink.send_window_event(
|
||||
crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64),
|
||||
window.wid,
|
||||
);
|
||||
}
|
||||
if window.refresh {
|
||||
sink.send_window_event(crate::event::WindowEvent::RedrawRequested, window.wid);
|
||||
}
|
||||
if window.closed {
|
||||
sink.send_window_event(crate::event::WindowEvent::CloseRequested, window.wid);
|
||||
}
|
||||
|
||||
if let Some(grab_cursor) = grab_cursor {
|
||||
let surface = if grab_cursor { Some(surface) } else { None };
|
||||
self.cursor_manager.lock().unwrap().grab_pointer(surface);
|
||||
}
|
||||
},
|
||||
)
|
||||
if let Some(grab_cursor) = window.grab_cursor {
|
||||
let surface = if grab_cursor {
|
||||
Some(window.surface)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.cursor_manager.lock().unwrap().grab_pointer(surface);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,7 +728,7 @@ struct SeatManager<T: 'static> {
|
||||
sink: Arc<Mutex<WindowEventsSink<T>>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
|
||||
kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
|
||||
kbd_sender: ::calloop::channel::Sender<crate::event::Event<()>>,
|
||||
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
|
||||
pointer_constraints_proxy: Arc<Mutex<Option<ZwpPointerConstraintsV1>>>,
|
||||
cursor_manager: Arc<Mutex<CursorManager>>,
|
||||
@@ -764,7 +773,7 @@ impl<T: 'static> SeatManager<T> {
|
||||
struct SeatData<T> {
|
||||
sink: Arc<Mutex<WindowEventsSink<T>>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
|
||||
kbd_sender: ::calloop::channel::Sender<crate::event::Event<()>>,
|
||||
pointer: Option<wl_pointer::WlPointer>,
|
||||
relative_pointer: Option<ZwpRelativePointerV1>,
|
||||
relative_pointer_manager_proxy: Rc<RefCell<Option<ZwpRelativePointerManagerV1>>>,
|
||||
|
||||
@@ -8,11 +8,13 @@ use smithay_client_toolkit::{
|
||||
reexports::client::protocol::{wl_keyboard, wl_seat},
|
||||
};
|
||||
|
||||
use crate::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
|
||||
use crate::event::{
|
||||
DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent,
|
||||
};
|
||||
|
||||
pub fn init_keyboard(
|
||||
seat: &wl_seat::WlSeat,
|
||||
sink: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
|
||||
sink: ::calloop::channel::Sender<crate::event::Event<()>>,
|
||||
modifiers_tracker: Arc<Mutex<ModifiersState>>,
|
||||
) -> wl_keyboard::WlKeyboard {
|
||||
// { variables to be captured by the closures
|
||||
@@ -29,12 +31,22 @@ pub fn init_keyboard(
|
||||
match evt {
|
||||
KbEvent::Enter { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink.send((WindowEvent::Focused(true), wid)).unwrap();
|
||||
my_sink
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::Focused(true),
|
||||
})
|
||||
.unwrap();
|
||||
*target.lock().unwrap() = Some(wid);
|
||||
}
|
||||
KbEvent::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink.send((WindowEvent::Focused(false), wid)).unwrap();
|
||||
my_sink
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::Focused(false),
|
||||
})
|
||||
.unwrap();
|
||||
*target.lock().unwrap() = None;
|
||||
}
|
||||
KbEvent::Key {
|
||||
@@ -52,20 +64,19 @@ pub fn init_keyboard(
|
||||
};
|
||||
let vkcode = key_to_vkey(rawkey, keysym);
|
||||
my_sink
|
||||
.send((
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: device_id(),
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode: rawkey,
|
||||
virtual_keycode: vkcode,
|
||||
modifiers: modifiers_tracker.lock().unwrap().clone(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
wid,
|
||||
))
|
||||
})
|
||||
.unwrap();
|
||||
// send char event only on key press, not release
|
||||
if let ElementState::Released = state {
|
||||
@@ -74,7 +85,10 @@ pub fn init_keyboard(
|
||||
if let Some(txt) = utf8 {
|
||||
for chr in txt.chars() {
|
||||
my_sink
|
||||
.send((WindowEvent::ReceivedCharacter(chr), wid))
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::ReceivedCharacter(chr),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -84,15 +98,16 @@ pub fn init_keyboard(
|
||||
KbEvent::Modifiers {
|
||||
modifiers: event_modifiers,
|
||||
} => {
|
||||
let modifiers = event_modifiers.into();
|
||||
let modifiers = ModifiersState::from_wayland(event_modifiers);
|
||||
|
||||
*modifiers_tracker.lock().unwrap() = modifiers;
|
||||
|
||||
if let Some(wid) = *target.lock().unwrap() {
|
||||
my_sink
|
||||
.send((WindowEvent::ModifiersChanged { modifiers }, wid))
|
||||
.unwrap();
|
||||
}
|
||||
my_sink
|
||||
.send(Event::DeviceEvent {
|
||||
device_id: device_id(),
|
||||
event: DeviceEvent::ModifiersChanged { modifiers },
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -101,25 +116,27 @@ pub fn init_keyboard(
|
||||
let state = ElementState::Pressed;
|
||||
let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym);
|
||||
repeat_sink
|
||||
.send((
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: device_id(),
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode: repeat_event.rawkey,
|
||||
virtual_keycode: vkcode,
|
||||
modifiers: my_modifiers.lock().unwrap().clone(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
wid,
|
||||
))
|
||||
})
|
||||
.unwrap();
|
||||
if let Some(txt) = repeat_event.utf8 {
|
||||
for chr in txt.chars() {
|
||||
repeat_sink
|
||||
.send((WindowEvent::ReceivedCharacter(chr), wid))
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::ReceivedCharacter(chr),
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -147,12 +164,22 @@ pub fn init_keyboard(
|
||||
move |evt, _| match evt {
|
||||
wl_keyboard::Event::Enter { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink.send((WindowEvent::Focused(true), wid)).unwrap();
|
||||
my_sink
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::Focused(true),
|
||||
})
|
||||
.unwrap();
|
||||
target = Some(wid);
|
||||
}
|
||||
wl_keyboard::Event::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink.send((WindowEvent::Focused(false), wid)).unwrap();
|
||||
my_sink
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::Focused(false),
|
||||
})
|
||||
.unwrap();
|
||||
target = None;
|
||||
}
|
||||
wl_keyboard::Event::Key { key, state, .. } => {
|
||||
@@ -163,20 +190,19 @@ pub fn init_keyboard(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
my_sink
|
||||
.send((
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: crate::event::DeviceId(
|
||||
crate::platform_impl::DeviceId::Wayland(DeviceId),
|
||||
),
|
||||
.send(Event::WindowEvent {
|
||||
window_id: mk_root_wid(wid),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: device_id(),
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode: key,
|
||||
virtual_keycode: None,
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
wid,
|
||||
))
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -376,8 +402,8 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<keyboard::ModifiersState> for ModifiersState {
|
||||
fn from(mods: keyboard::ModifiersState) -> ModifiersState {
|
||||
impl ModifiersState {
|
||||
pub(crate) fn from_wayland(mods: keyboard::ModifiersState) -> ModifiersState {
|
||||
ModifiersState {
|
||||
shift: mods.shift,
|
||||
ctrl: mods.ctrl,
|
||||
@@ -386,3 +412,11 @@ impl From<keyboard::ModifiersState> for ModifiersState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn device_id() -> crate::event::DeviceId {
|
||||
crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId))
|
||||
}
|
||||
|
||||
fn mk_root_wid(wid: crate::platform_impl::wayland::WindowId) -> crate::window::WindowId {
|
||||
crate::window::WindowId(crate::platform_impl::WindowId::Wayland(wid))
|
||||
}
|
||||
|
||||
@@ -385,6 +385,19 @@ pub struct WindowStore {
|
||||
windows: Vec<InternalWindow>,
|
||||
}
|
||||
|
||||
pub struct WindowStoreForEach<'a> {
|
||||
pub newsize: Option<(u32, u32)>,
|
||||
pub size: &'a mut (u32, u32),
|
||||
pub new_dpi: Option<i32>,
|
||||
pub refresh: bool,
|
||||
pub frame_refresh: bool,
|
||||
pub closed: bool,
|
||||
pub grab_cursor: Option<bool>,
|
||||
pub surface: &'a wl_surface::WlSurface,
|
||||
pub wid: WindowId,
|
||||
pub frame: Option<&'a mut SWindow<ConceptFrame>>,
|
||||
}
|
||||
|
||||
impl WindowStore {
|
||||
pub fn new() -> WindowStore {
|
||||
WindowStore {
|
||||
@@ -434,34 +447,23 @@ impl WindowStore {
|
||||
|
||||
pub fn for_each<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(
|
||||
Option<(u32, u32)>,
|
||||
&mut (u32, u32),
|
||||
Option<i32>,
|
||||
bool,
|
||||
bool,
|
||||
bool,
|
||||
Option<bool>,
|
||||
&wl_surface::WlSurface,
|
||||
WindowId,
|
||||
Option<&mut SWindow<ConceptFrame>>,
|
||||
),
|
||||
F: FnMut(WindowStoreForEach<'_>),
|
||||
{
|
||||
for window in &mut self.windows {
|
||||
let opt_arc = window.frame.upgrade();
|
||||
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
|
||||
f(
|
||||
window.newsize.take(),
|
||||
&mut *(window.size.lock().unwrap()),
|
||||
window.new_dpi,
|
||||
replace(&mut *window.need_refresh.lock().unwrap(), false),
|
||||
replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
||||
window.closed,
|
||||
window.cursor_grab_changed.lock().unwrap().take(),
|
||||
&window.surface,
|
||||
make_wid(&window.surface),
|
||||
opt_mutex_lock.as_mut().map(|m| &mut **m),
|
||||
);
|
||||
f(WindowStoreForEach {
|
||||
newsize: window.newsize.take(),
|
||||
size: &mut *(window.size.lock().unwrap()),
|
||||
new_dpi: window.new_dpi,
|
||||
refresh: replace(&mut *window.need_refresh.lock().unwrap(), false),
|
||||
frame_refresh: replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
||||
closed: window.closed,
|
||||
grab_cursor: window.cursor_grab_changed.lock().unwrap().take(),
|
||||
surface: &window.surface,
|
||||
wid: make_wid(&window.surface),
|
||||
frame: opt_mutex_lock.as_mut().map(|m| &mut **m),
|
||||
});
|
||||
if let Some(dpi) = window.new_dpi.take() {
|
||||
window.current_dpi = dpi;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{cell::RefCell, collections::HashMap, ptr, rc::Rc, slice};
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice};
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
|
||||
|
||||
@@ -12,7 +12,7 @@ use util::modifiers::{ModifierKeyState, ModifierKeymap};
|
||||
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
event_loop::EventLoopWindowTarget as RootELW,
|
||||
};
|
||||
|
||||
@@ -25,7 +25,6 @@ pub(super) struct EventProcessor<T: 'static> {
|
||||
pub(super) target: Rc<RootELW<T>>,
|
||||
pub(super) mod_keymap: ModifierKeymap,
|
||||
pub(super) device_mod_state: ModifierKeyState,
|
||||
pub(super) window_mod_state: ModifierKeyState,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventProcessor<T> {
|
||||
@@ -67,6 +66,13 @@ impl<T: 'static> EventProcessor<T> {
|
||||
self.with_window(window_id, |_| ()).is_some()
|
||||
}
|
||||
|
||||
pub(super) fn poll(&self) -> bool {
|
||||
let wt = get_xtarget(&self.target);
|
||||
let result = unsafe { (wt.xconn.xlib.XPending)(wt.xconn.display) };
|
||||
|
||||
result != 0
|
||||
}
|
||||
|
||||
pub(super) unsafe fn poll_one_event(&mut self, event_ptr: *mut ffi::XEvent) -> bool {
|
||||
let wt = get_xtarget(&self.target);
|
||||
// This function is used to poll and remove a single event
|
||||
@@ -114,6 +120,26 @@ impl<T: 'static> EventProcessor<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't call a `&mut self` method because of the above borrow,
|
||||
// so we use this macro for repeated modifier state updates.
|
||||
macro_rules! update_modifiers {
|
||||
( $state:expr , $modifier:expr ) => {{
|
||||
match ($state, $modifier) {
|
||||
(state, modifier) => {
|
||||
if let Some(modifiers) =
|
||||
self.device_mod_state.update_state(&state, modifier)
|
||||
{
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::ModifiersChanged { modifiers },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
let event_type = xev.get_type();
|
||||
match event_type {
|
||||
ffi::MappingNotify => {
|
||||
@@ -130,8 +156,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
.expect("Failed to call XRefreshKeyboardMapping");
|
||||
|
||||
self.mod_keymap.reset_from_x_connection(&wt.xconn);
|
||||
self.device_mod_state.update(&self.mod_keymap);
|
||||
self.window_mod_state.update(&self.mod_keymap);
|
||||
self.device_mod_state.update_keymap(&self.mod_keymap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,24 +402,20 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let (width, height) = shared_state_lock
|
||||
.dpi_adjusted
|
||||
.unwrap_or_else(|| (xev.width as f64, xev.height as f64));
|
||||
let last_hidpi_factor =
|
||||
shared_state_lock.guessed_dpi.take().unwrap_or_else(|| {
|
||||
shared_state_lock
|
||||
.last_monitor
|
||||
.as_ref()
|
||||
.map(|last_monitor| last_monitor.hidpi_factor)
|
||||
.unwrap_or(1.0)
|
||||
});
|
||||
|
||||
let last_hidpi_factor = shared_state_lock.last_monitor.hidpi_factor;
|
||||
let new_hidpi_factor = {
|
||||
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
|
||||
monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
|
||||
let new_hidpi_factor = monitor.hidpi_factor;
|
||||
let new_monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
|
||||
|
||||
// Avoid caching an invalid dummy monitor handle
|
||||
if monitor.id != 0 {
|
||||
shared_state_lock.last_monitor = Some(monitor.clone());
|
||||
if new_monitor.is_dummy() {
|
||||
// Avoid updating monitor using a dummy monitor handle
|
||||
last_hidpi_factor
|
||||
} else {
|
||||
monitor = new_monitor;
|
||||
shared_state_lock.last_monitor = monitor.clone();
|
||||
monitor.hidpi_factor
|
||||
}
|
||||
new_hidpi_factor
|
||||
};
|
||||
if last_hidpi_factor != new_hidpi_factor {
|
||||
events.dpi_changed =
|
||||
@@ -498,6 +519,13 @@ impl<T: 'static> EventProcessor<T> {
|
||||
});
|
||||
}
|
||||
|
||||
ffi::VisibilityNotify => {
|
||||
let xev: &ffi::XVisibilityEvent = xev.as_ref();
|
||||
let xwindow = xev.window;
|
||||
|
||||
self.with_window(xwindow, |window| window.visibility_notify());
|
||||
}
|
||||
|
||||
ffi::Expose => {
|
||||
let xev: &ffi::XExposeEvent = xev.as_ref();
|
||||
|
||||
@@ -529,25 +557,21 @@ impl<T: 'static> EventProcessor<T> {
|
||||
// value, though this should only be an issue under multiseat configurations.
|
||||
let device = util::VIRTUAL_CORE_KEYBOARD;
|
||||
let device_id = mkdid(device);
|
||||
let keycode = xkev.keycode;
|
||||
|
||||
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
|
||||
// a keycode of 0.
|
||||
if xkev.keycode != 0 {
|
||||
let keysym = unsafe {
|
||||
let mut keysym = 0;
|
||||
(wt.xconn.xlib.XLookupString)(
|
||||
xkev,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
&mut keysym,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
wt.xconn.check_errors().expect("Failed to lookup keysym");
|
||||
keysym
|
||||
};
|
||||
if keycode != 0 {
|
||||
let scancode = keycode - 8;
|
||||
let keysym = wt.xconn.lookup_keysym(xkev);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
let modifiers = self.window_mod_state.modifiers();
|
||||
update_modifiers!(
|
||||
ModifiersState::from_x11_mask(xkev.state),
|
||||
self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode)
|
||||
);
|
||||
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
@@ -555,33 +579,13 @@ impl<T: 'static> EventProcessor<T> {
|
||||
device_id,
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode: xkev.keycode - 8,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
|
||||
if let Some(modifier) =
|
||||
self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode)
|
||||
{
|
||||
self.window_mod_state.key_event(
|
||||
state,
|
||||
xkev.keycode as ffi::KeyCode,
|
||||
modifier,
|
||||
);
|
||||
|
||||
let new_modifiers = self.window_mod_state.modifiers();
|
||||
|
||||
if modifiers != new_modifiers {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged {
|
||||
modifiers: new_modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state == Pressed {
|
||||
@@ -633,7 +637,8 @@ impl<T: 'static> EventProcessor<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
let modifiers = ModifiersState::from(xev.mods);
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
update_modifiers!(modifiers, None);
|
||||
|
||||
let state = if xev.evtype == ffi::XI_ButtonPress {
|
||||
Pressed
|
||||
@@ -709,7 +714,8 @@ impl<T: 'static> EventProcessor<T> {
|
||||
let window_id = mkwid(xev.event);
|
||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||
|
||||
let modifiers = ModifiersState::from(xev.mods);
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
update_modifiers!(modifiers, None);
|
||||
|
||||
let cursor_moved = self.with_window(xev.event, |window| {
|
||||
let mut shared_state_lock = window.shared_state.lock();
|
||||
@@ -894,20 +900,9 @@ impl<T: 'static> EventProcessor<T> {
|
||||
event: Focused(true),
|
||||
});
|
||||
|
||||
// When focus is gained, send any existing modifiers
|
||||
// to the window in a ModifiersChanged event. This is
|
||||
// done to compensate for modifier keys that may be
|
||||
// changed while a window is out of focus.
|
||||
if !self.device_mod_state.is_empty() {
|
||||
self.window_mod_state = self.device_mod_state.clone();
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
|
||||
let modifiers = self.window_mod_state.modifiers();
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged { modifiers },
|
||||
});
|
||||
}
|
||||
update_modifiers!(modifiers, None);
|
||||
|
||||
// The deviceid for this event is for a keyboard instead of a pointer,
|
||||
// so we have to do a little extra work.
|
||||
@@ -927,9 +922,12 @@ impl<T: 'static> EventProcessor<T> {
|
||||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
position,
|
||||
modifiers: ModifiersState::from(xev.mods),
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
|
||||
// Issue key press events for all pressed keys
|
||||
self.handle_pressed_keys(window_id, ElementState::Pressed, &mut callback);
|
||||
}
|
||||
ffi::XI_FocusOut => {
|
||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||
@@ -941,23 +939,13 @@ impl<T: 'static> EventProcessor<T> {
|
||||
.unfocus(xev.event)
|
||||
.expect("Failed to unfocus input context");
|
||||
|
||||
// When focus is lost, send a ModifiersChanged event
|
||||
// containing no modifiers set. This is done to compensate
|
||||
// for modifier keys that may be changed while a window
|
||||
// is out of focus.
|
||||
if !self.window_mod_state.is_empty() {
|
||||
self.window_mod_state.clear();
|
||||
let window_id = mkwid(xev.event);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xev.event),
|
||||
event: WindowEvent::ModifiersChanged {
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
});
|
||||
}
|
||||
// Issue key release events for all pressed keys
|
||||
self.handle_pressed_keys(window_id, ElementState::Released, &mut callback);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xev.event),
|
||||
window_id,
|
||||
event: Focused(false),
|
||||
})
|
||||
}
|
||||
@@ -1068,25 +1056,25 @@ impl<T: 'static> EventProcessor<T> {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let device_id = xev.sourceid;
|
||||
let device_id = mkdid(xev.sourceid);
|
||||
let keycode = xev.detail;
|
||||
if keycode < 8 {
|
||||
return;
|
||||
}
|
||||
let scancode = (keycode - 8) as u32;
|
||||
|
||||
let keysym = unsafe {
|
||||
(wt.xconn.xlib.XKeycodeToKeysym)(
|
||||
wt.xconn.display,
|
||||
xev.detail as ffi::KeyCode,
|
||||
0,
|
||||
)
|
||||
};
|
||||
wt.xconn
|
||||
.check_errors()
|
||||
.expect("Failed to lookup raw keysym");
|
||||
|
||||
let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::Key(KeyboardInput {
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
state,
|
||||
modifiers,
|
||||
}),
|
||||
});
|
||||
|
||||
if let Some(modifier) =
|
||||
self.mod_keymap.get_modifier(keycode as ffi::KeyCode)
|
||||
@@ -1096,19 +1084,18 @@ impl<T: 'static> EventProcessor<T> {
|
||||
keycode as ffi::KeyCode,
|
||||
modifier,
|
||||
);
|
||||
|
||||
let new_modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
if modifiers != new_modifiers {
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::ModifiersChanged {
|
||||
modifiers: new_modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(device_id),
|
||||
event: DeviceEvent::Key(KeyboardInput {
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
state,
|
||||
modifiers,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
ffi::XI_HierarchyChanged => {
|
||||
@@ -1187,4 +1174,45 @@ impl<T: 'static> EventProcessor<T> {
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_pressed_keys<F>(
|
||||
&self,
|
||||
window_id: crate::window::WindowId,
|
||||
state: ElementState,
|
||||
callback: &mut F,
|
||||
) where
|
||||
F: FnMut(Event<T>),
|
||||
{
|
||||
let wt = get_xtarget(&self.target);
|
||||
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
// Get the set of keys currently pressed and apply Key events to each
|
||||
let keys = wt.xconn.query_keymap();
|
||||
|
||||
for keycode in &keys {
|
||||
if keycode < 8 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let scancode = (keycode - 8) as u32;
|
||||
let keysym = wt.xconn.keycode_to_keysym(keycode);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
input: KeyboardInput {
|
||||
scancode,
|
||||
state,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
#![cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
|
||||
mod dnd;
|
||||
mod event_processor;
|
||||
@@ -26,6 +32,7 @@ use std::{
|
||||
rc::Rc,
|
||||
slice,
|
||||
sync::{mpsc, Arc, Mutex, Weak},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use libc::{self, setlocale, LC_CTYPE};
|
||||
@@ -38,7 +45,7 @@ use self::{
|
||||
};
|
||||
use crate::{
|
||||
error::OsError as RootOsError,
|
||||
event::{Event, WindowEvent},
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes},
|
||||
window::WindowAttributes,
|
||||
@@ -192,7 +199,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
xi2ext,
|
||||
mod_keymap,
|
||||
device_mod_state: Default::default(),
|
||||
window_mod_state: Default::default(),
|
||||
};
|
||||
|
||||
// Register for device hotplug events
|
||||
@@ -263,6 +269,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
);
|
||||
|
||||
loop {
|
||||
self.drain_events();
|
||||
|
||||
// Empty the event buffer
|
||||
{
|
||||
let mut guard = self.pending_events.borrow_mut();
|
||||
@@ -310,69 +318,58 @@ impl<T: 'static> EventLoop<T> {
|
||||
);
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let (mut cause, deadline, mut timeout);
|
||||
|
||||
match control_flow {
|
||||
ControlFlow::Exit => break,
|
||||
ControlFlow::Poll => {
|
||||
// non-blocking dispatch
|
||||
self.inner_loop
|
||||
.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ())
|
||||
.unwrap();
|
||||
callback(
|
||||
crate::event::Event::NewEvents(crate::event::StartCause::Poll),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
);
|
||||
cause = StartCause::Poll;
|
||||
deadline = None;
|
||||
timeout = Some(Duration::from_millis(0));
|
||||
}
|
||||
ControlFlow::Wait => {
|
||||
self.inner_loop.dispatch(None, &mut ()).unwrap();
|
||||
callback(
|
||||
crate::event::Event::NewEvents(crate::event::StartCause::WaitCancelled {
|
||||
start: ::std::time::Instant::now(),
|
||||
requested_resume: None,
|
||||
}),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
);
|
||||
}
|
||||
ControlFlow::WaitUntil(deadline) => {
|
||||
let start = ::std::time::Instant::now();
|
||||
// compute the blocking duration
|
||||
let duration = if deadline > start {
|
||||
deadline - start
|
||||
} else {
|
||||
::std::time::Duration::from_millis(0)
|
||||
cause = StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: None,
|
||||
};
|
||||
self.inner_loop.dispatch(Some(duration), &mut ()).unwrap();
|
||||
let now = std::time::Instant::now();
|
||||
if now < deadline {
|
||||
callback(
|
||||
crate::event::Event::NewEvents(
|
||||
crate::event::StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: Some(deadline),
|
||||
},
|
||||
),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
);
|
||||
deadline = None;
|
||||
timeout = None;
|
||||
}
|
||||
ControlFlow::WaitUntil(wait_deadline) => {
|
||||
cause = StartCause::ResumeTimeReached {
|
||||
start,
|
||||
requested_resume: wait_deadline,
|
||||
};
|
||||
timeout = if wait_deadline > start {
|
||||
Some(wait_deadline - start)
|
||||
} else {
|
||||
callback(
|
||||
crate::event::Event::NewEvents(
|
||||
crate::event::StartCause::ResumeTimeReached {
|
||||
start,
|
||||
requested_resume: deadline,
|
||||
},
|
||||
),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
);
|
||||
}
|
||||
Some(Duration::from_millis(0))
|
||||
};
|
||||
deadline = Some(wait_deadline);
|
||||
}
|
||||
}
|
||||
|
||||
// If the user callback had any interaction with the X server,
|
||||
// it may have received and buffered some user input events.
|
||||
self.drain_events();
|
||||
if self.events_waiting() {
|
||||
timeout = Some(Duration::from_millis(0));
|
||||
}
|
||||
|
||||
self.inner_loop.dispatch(timeout, &mut ()).unwrap();
|
||||
|
||||
if let Some(deadline) = deadline {
|
||||
if deadline > Instant::now() {
|
||||
cause = StartCause::WaitCancelled {
|
||||
start,
|
||||
requested_resume: Some(deadline),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
callback(
|
||||
crate::event::Event::NewEvents(cause),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
);
|
||||
}
|
||||
|
||||
callback(
|
||||
@@ -396,6 +393,10 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
drain_events(&mut processor, &mut pending_events);
|
||||
}
|
||||
|
||||
fn events_waiting(&self) -> bool {
|
||||
!self.pending_events.borrow().is_empty() || self.event_processor.borrow().poll()
|
||||
}
|
||||
}
|
||||
|
||||
fn drain_events<T: 'static>(
|
||||
@@ -430,8 +431,14 @@ impl<T> EventLoopWindowTarget<T> {
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
self.user_sender.send(event).map_err(|_| EventLoopClosed)
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.user_sender.send(event).map_err(|e| {
|
||||
EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e {
|
||||
x
|
||||
} else {
|
||||
unreachable!()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ impl MonitorHandle {
|
||||
})
|
||||
}
|
||||
|
||||
fn dummy() -> Self {
|
||||
pub fn dummy() -> Self {
|
||||
MonitorHandle {
|
||||
id: 0,
|
||||
name: "<dummy monitor>".into(),
|
||||
@@ -143,6 +143,11 @@ impl MonitorHandle {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_dummy(&self) -> bool {
|
||||
// Zero is an invalid XID value; no real monitor will have it
|
||||
self.id == 0
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<String> {
|
||||
Some(self.name.clone())
|
||||
}
|
||||
|
||||
@@ -11,14 +11,17 @@ pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
|
||||
// To test if `lookup_utf8` works correctly, set this to 1.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
impl From<ffi::XIModifierState> for ModifiersState {
|
||||
fn from(mods: ffi::XIModifierState) -> Self {
|
||||
let state = mods.effective as c_uint;
|
||||
impl ModifiersState {
|
||||
pub(crate) fn from_x11(state: &ffi::XIModifierState) -> Self {
|
||||
ModifiersState::from_x11_mask(state.effective as c_uint)
|
||||
}
|
||||
|
||||
pub(crate) fn from_x11_mask(mask: c_uint) -> Self {
|
||||
ModifiersState {
|
||||
alt: state & ffi::Mod1Mask != 0,
|
||||
shift: state & ffi::ShiftMask != 0,
|
||||
ctrl: state & ffi::ControlMask != 0,
|
||||
logo: state & ffi::Mod4Mask != 0,
|
||||
alt: mask & ffi::Mod1Mask != 0,
|
||||
shift: mask & ffi::ShiftMask != 0,
|
||||
ctrl: mask & ffi::ControlMask != 0,
|
||||
logo: mask & ffi::Mod4Mask != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +43,7 @@ pub struct PointerState<'a> {
|
||||
|
||||
impl<'a> PointerState<'a> {
|
||||
pub fn get_modifier_state(&self) -> ModifiersState {
|
||||
self.modifiers.into()
|
||||
ModifiersState::from_x11(&self.modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
92
src/platform_impl/linux/x11/util/keys.rs
Normal file
92
src/platform_impl/linux/x11/util/keys.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
use std::{iter::Enumerate, ptr, slice::Iter};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct Keymap {
|
||||
keys: [u8; 32],
|
||||
}
|
||||
|
||||
pub struct KeymapIter<'a> {
|
||||
iter: Enumerate<Iter<'a, u8>>,
|
||||
index: usize,
|
||||
item: Option<u8>,
|
||||
}
|
||||
|
||||
impl Keymap {
|
||||
pub fn iter(&self) -> KeymapIter<'_> {
|
||||
KeymapIter {
|
||||
iter: self.keys.iter().enumerate(),
|
||||
index: 0,
|
||||
item: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Keymap {
|
||||
type Item = ffi::KeyCode;
|
||||
type IntoIter = KeymapIter<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for KeymapIter<'_> {
|
||||
type Item = ffi::KeyCode;
|
||||
|
||||
fn next(&mut self) -> Option<ffi::KeyCode> {
|
||||
if self.item.is_none() {
|
||||
while let Some((index, &item)) = self.iter.next() {
|
||||
if item != 0 {
|
||||
self.index = index;
|
||||
self.item = Some(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.item.take().map(|item| {
|
||||
debug_assert!(item != 0);
|
||||
|
||||
let bit = first_bit(item);
|
||||
|
||||
if item != bit {
|
||||
// Remove the first bit; save the rest for further iterations
|
||||
self.item = Some(item ^ bit);
|
||||
}
|
||||
|
||||
let shift = bit.trailing_zeros() + (self.index * 8) as u32;
|
||||
shift as ffi::KeyCode
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn keycode_to_keysym(&self, keycode: ffi::KeyCode) -> ffi::KeySym {
|
||||
unsafe { (self.xlib.XKeycodeToKeysym)(self.display, keycode, 0) }
|
||||
}
|
||||
|
||||
pub fn lookup_keysym(&self, xkev: &mut ffi::XKeyEvent) -> ffi::KeySym {
|
||||
let mut keysym = 0;
|
||||
|
||||
unsafe {
|
||||
(self.xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut());
|
||||
}
|
||||
|
||||
keysym
|
||||
}
|
||||
|
||||
pub fn query_keymap(&self) -> Keymap {
|
||||
let mut keys = [0; 32];
|
||||
|
||||
unsafe {
|
||||
(self.xlib.XQueryKeymap)(self.display, keys.as_mut_ptr() as *mut c_char);
|
||||
}
|
||||
|
||||
Keymap { keys }
|
||||
}
|
||||
}
|
||||
|
||||
fn first_bit(b: u8) -> u8 {
|
||||
1 << b.trailing_zeros()
|
||||
}
|
||||
@@ -9,6 +9,7 @@ mod geometry;
|
||||
mod hint;
|
||||
mod icon;
|
||||
mod input;
|
||||
pub mod keys;
|
||||
mod memory;
|
||||
pub mod modifiers;
|
||||
mod randr;
|
||||
|
||||
@@ -35,6 +35,7 @@ pub struct ModifierKeymap {
|
||||
pub struct ModifierKeyState {
|
||||
// Contains currently pressed modifier keys and their corresponding modifiers
|
||||
keys: HashMap<ffi::KeyCode, Modifier>,
|
||||
state: ModifiersState,
|
||||
}
|
||||
|
||||
impl ModifierKeymap {
|
||||
@@ -94,15 +95,7 @@ impl ModifierKeymap {
|
||||
}
|
||||
|
||||
impl ModifierKeyState {
|
||||
pub fn clear(&mut self) {
|
||||
self.keys.clear();
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.keys.is_empty()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, mods: &ModifierKeymap) {
|
||||
pub fn update_keymap(&mut self, mods: &ModifierKeymap) {
|
||||
self.keys.retain(|k, v| {
|
||||
if let Some(m) = mods.get_modifier(*k) {
|
||||
*v = m;
|
||||
@@ -111,16 +104,36 @@ impl ModifierKeyState {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
self.reset_state();
|
||||
}
|
||||
|
||||
pub fn update_state(
|
||||
&mut self,
|
||||
state: &ModifiersState,
|
||||
except: Option<Modifier>,
|
||||
) -> Option<ModifiersState> {
|
||||
let mut new_state = *state;
|
||||
|
||||
match except {
|
||||
Some(Modifier::Alt) => new_state.alt = self.state.alt,
|
||||
Some(Modifier::Ctrl) => new_state.ctrl = self.state.ctrl,
|
||||
Some(Modifier::Shift) => new_state.shift = self.state.shift,
|
||||
Some(Modifier::Logo) => new_state.logo = self.state.logo,
|
||||
None => (),
|
||||
}
|
||||
|
||||
if self.state == new_state {
|
||||
None
|
||||
} else {
|
||||
self.keys.retain(|_k, v| get_modifier(&new_state, *v));
|
||||
self.state = new_state;
|
||||
Some(new_state)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modifiers(&self) -> ModifiersState {
|
||||
let mut state = ModifiersState::default();
|
||||
|
||||
for &m in self.keys.values() {
|
||||
set_modifier(&mut state, m);
|
||||
}
|
||||
|
||||
state
|
||||
self.state
|
||||
}
|
||||
|
||||
pub fn key_event(&mut self, state: ElementState, keycode: ffi::KeyCode, modifier: Modifier) {
|
||||
@@ -132,18 +145,43 @@ impl ModifierKeyState {
|
||||
|
||||
pub fn key_press(&mut self, keycode: ffi::KeyCode, modifier: Modifier) {
|
||||
self.keys.insert(keycode, modifier);
|
||||
|
||||
set_modifier(&mut self.state, modifier, true);
|
||||
}
|
||||
|
||||
pub fn key_release(&mut self, keycode: ffi::KeyCode) {
|
||||
self.keys.remove(&keycode);
|
||||
if let Some(modifier) = self.keys.remove(&keycode) {
|
||||
if self.keys.values().find(|&&m| m == modifier).is_none() {
|
||||
set_modifier(&mut self.state, modifier, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_state(&mut self) {
|
||||
let mut new_state = ModifiersState::default();
|
||||
|
||||
for &m in self.keys.values() {
|
||||
set_modifier(&mut new_state, m, true);
|
||||
}
|
||||
|
||||
self.state = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
fn set_modifier(state: &mut ModifiersState, modifier: Modifier) {
|
||||
fn get_modifier(state: &ModifiersState, modifier: Modifier) -> bool {
|
||||
match modifier {
|
||||
Modifier::Alt => state.alt = true,
|
||||
Modifier::Ctrl => state.ctrl = true,
|
||||
Modifier::Shift => state.shift = true,
|
||||
Modifier::Logo => state.logo = true,
|
||||
Modifier::Alt => state.alt,
|
||||
Modifier::Ctrl => state.ctrl,
|
||||
Modifier::Shift => state.shift,
|
||||
Modifier::Logo => state.logo,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_modifier(state: &mut ModifiersState, modifier: Modifier, value: bool) {
|
||||
match modifier {
|
||||
Modifier::Alt => state.alt = value,
|
||||
Modifier::Ctrl => state.ctrl = value,
|
||||
Modifier::Shift => state.shift = value,
|
||||
Modifier::Logo => state.logo = value,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,27 +28,18 @@ use crate::{
|
||||
|
||||
use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError};
|
||||
|
||||
unsafe extern "C" fn visibility_predicate(
|
||||
_display: *mut ffi::Display,
|
||||
event: *mut ffi::XEvent,
|
||||
arg: ffi::XPointer, // We populate this with the window ID (by value) when we call XIfEvent
|
||||
) -> ffi::Bool {
|
||||
let event: &ffi::XAnyEvent = (*event).as_ref();
|
||||
let window = arg as ffi::Window;
|
||||
(event.window == window && event.type_ == ffi::VisibilityNotify) as _
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
pub struct SharedState {
|
||||
pub cursor_pos: Option<(f64, f64)>,
|
||||
pub size: Option<(u32, u32)>,
|
||||
pub position: Option<(i32, i32)>,
|
||||
pub inner_position: Option<(i32, i32)>,
|
||||
pub inner_position_rel_parent: Option<(i32, i32)>,
|
||||
pub guessed_dpi: Option<f64>,
|
||||
pub last_monitor: Option<X11MonitorHandle>,
|
||||
pub last_monitor: X11MonitorHandle,
|
||||
pub dpi_adjusted: Option<(f64, f64)>,
|
||||
pub fullscreen: Option<Fullscreen>,
|
||||
// Set when application calls `set_fullscreen` when window is not visible
|
||||
pub desired_fullscreen: Option<Option<Fullscreen>>,
|
||||
// Used to restore position after exiting fullscreen
|
||||
pub restore_position: Option<(i32, i32)>,
|
||||
// Used to restore video mode after exiting fullscreen
|
||||
@@ -56,15 +47,43 @@ pub struct SharedState {
|
||||
pub frame_extents: Option<util::FrameExtentsHeuristic>,
|
||||
pub min_inner_size: Option<LogicalSize>,
|
||||
pub max_inner_size: Option<LogicalSize>,
|
||||
pub is_visible: bool,
|
||||
pub visibility: Visibility,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Visibility {
|
||||
No,
|
||||
Yes,
|
||||
// Waiting for VisibilityNotify
|
||||
YesWait,
|
||||
}
|
||||
|
||||
impl SharedState {
|
||||
fn new(dpi_factor: f64, is_visible: bool) -> Mutex<Self> {
|
||||
let mut shared_state = SharedState::default();
|
||||
shared_state.guessed_dpi = Some(dpi_factor);
|
||||
shared_state.is_visible = is_visible;
|
||||
Mutex::new(shared_state)
|
||||
fn new(last_monitor: X11MonitorHandle, is_visible: bool) -> Mutex<Self> {
|
||||
let visibility = if is_visible {
|
||||
Visibility::YesWait
|
||||
} else {
|
||||
Visibility::No
|
||||
};
|
||||
|
||||
Mutex::new(SharedState {
|
||||
last_monitor,
|
||||
visibility,
|
||||
|
||||
cursor_pos: None,
|
||||
size: None,
|
||||
position: None,
|
||||
inner_position: None,
|
||||
inner_position_rel_parent: None,
|
||||
dpi_adjusted: None,
|
||||
fullscreen: None,
|
||||
desired_fullscreen: None,
|
||||
restore_position: None,
|
||||
desktop_video_mode: None,
|
||||
frame_extents: None,
|
||||
min_inner_size: None,
|
||||
max_inner_size: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,34 +112,27 @@ impl UnownedWindow {
|
||||
let xconn = &event_loop.xconn;
|
||||
let root = event_loop.root;
|
||||
|
||||
let monitors = xconn.available_monitors();
|
||||
let dpi_factor = if !monitors.is_empty() {
|
||||
let mut dpi_factor = Some(monitors[0].hidpi_factor());
|
||||
for monitor in &monitors {
|
||||
if Some(monitor.hidpi_factor()) != dpi_factor {
|
||||
dpi_factor = None;
|
||||
}
|
||||
}
|
||||
dpi_factor.unwrap_or_else(|| {
|
||||
xconn
|
||||
.query_pointer(root, util::VIRTUAL_CORE_POINTER)
|
||||
.ok()
|
||||
.and_then(|pointer_state| {
|
||||
let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
|
||||
let mut dpi_factor = None;
|
||||
for monitor in &monitors {
|
||||
if monitor.rect.contains_point(x, y) {
|
||||
dpi_factor = Some(monitor.hidpi_factor());
|
||||
break;
|
||||
}
|
||||
}
|
||||
dpi_factor
|
||||
})
|
||||
.unwrap_or(1.0)
|
||||
})
|
||||
let mut monitors = xconn.available_monitors();
|
||||
let guessed_monitor = if monitors.is_empty() {
|
||||
X11MonitorHandle::dummy()
|
||||
} else {
|
||||
1.0
|
||||
xconn
|
||||
.query_pointer(root, util::VIRTUAL_CORE_POINTER)
|
||||
.ok()
|
||||
.and_then(|pointer_state| {
|
||||
let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
|
||||
|
||||
for i in 0..monitors.len() {
|
||||
if monitors[i].rect.contains_point(x, y) {
|
||||
return Some(monitors.swap_remove(i));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.unwrap_or_else(|| monitors.swap_remove(0))
|
||||
};
|
||||
let dpi_factor = guessed_monitor.hidpi_factor();
|
||||
|
||||
info!("Guessed window DPI factor: {}", dpi_factor);
|
||||
|
||||
@@ -232,7 +244,7 @@ impl UnownedWindow {
|
||||
cursor_grabbed: Mutex::new(false),
|
||||
cursor_visible: Mutex::new(true),
|
||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||
shared_state: SharedState::new(dpi_factor, window_attrs.visible),
|
||||
shared_state: SharedState::new(guessed_monitor, window_attrs.visible),
|
||||
pending_redraws: event_loop.pending_redraws.clone(),
|
||||
};
|
||||
|
||||
@@ -355,8 +367,6 @@ impl UnownedWindow {
|
||||
unsafe {
|
||||
(xconn.xlib.XMapRaised)(xconn.display, window.xwindow);
|
||||
} //.queue();
|
||||
|
||||
window.wait_for_visibility_notify();
|
||||
}
|
||||
|
||||
// Attempt to make keyboard input repeat detectable
|
||||
@@ -414,8 +424,7 @@ impl UnownedWindow {
|
||||
if window_attrs.fullscreen.is_some() {
|
||||
window
|
||||
.set_fullscreen_inner(window_attrs.fullscreen.clone())
|
||||
.unwrap()
|
||||
.queue();
|
||||
.map(|flusher| flusher.queue());
|
||||
}
|
||||
if window_attrs.always_on_top {
|
||||
window
|
||||
@@ -572,9 +581,13 @@ impl UnownedWindow {
|
||||
fn set_fullscreen_inner(&self, fullscreen: Option<Fullscreen>) -> Option<util::Flusher<'_>> {
|
||||
let mut shared_state_lock = self.shared_state.lock();
|
||||
|
||||
if !shared_state_lock.is_visible {
|
||||
match shared_state_lock.visibility {
|
||||
// Setting fullscreen on a window that is not visible will generate an error.
|
||||
return None;
|
||||
Visibility::No | Visibility::YesWait => {
|
||||
shared_state_lock.desired_fullscreen = Some(fullscreen);
|
||||
return None;
|
||||
}
|
||||
Visibility::Yes => (),
|
||||
}
|
||||
|
||||
let old_fullscreen = shared_state_lock.fullscreen.clone();
|
||||
@@ -638,7 +651,7 @@ impl UnownedWindow {
|
||||
};
|
||||
|
||||
// Don't set fullscreen on an invalid dummy monitor handle
|
||||
if monitor.id == 0 {
|
||||
if monitor.is_dummy() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -685,7 +698,12 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
self.shared_state.lock().fullscreen.clone()
|
||||
let shared_state = self.shared_state.lock();
|
||||
|
||||
shared_state
|
||||
.desired_fullscreen
|
||||
.clone()
|
||||
.unwrap_or_else(|| shared_state.fullscreen.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -698,24 +716,29 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_rect(&self) -> util::AaRect {
|
||||
// TODO: This might round-trip more times than needed.
|
||||
let position = self.outer_position_physical();
|
||||
let size = self.outer_size_physical();
|
||||
util::AaRect::new(position, size)
|
||||
// Called by EventProcessor when a VisibilityNotify event is received
|
||||
pub(crate) fn visibility_notify(&self) {
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
|
||||
match shared_state.visibility {
|
||||
Visibility::No => unsafe {
|
||||
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
|
||||
},
|
||||
Visibility::Yes => (),
|
||||
Visibility::YesWait => {
|
||||
shared_state.visibility = Visibility::Yes;
|
||||
|
||||
if let Some(fullscreen) = shared_state.desired_fullscreen.take() {
|
||||
drop(shared_state);
|
||||
self.set_fullscreen(fullscreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_monitor(&self) -> X11MonitorHandle {
|
||||
let monitor = self.shared_state.lock().last_monitor.as_ref().cloned();
|
||||
monitor.unwrap_or_else(|| {
|
||||
let monitor = self.xconn.get_monitor_for_window(Some(self.get_rect()));
|
||||
// Avoid caching an invalid dummy monitor handle
|
||||
if monitor.id != 0 {
|
||||
self.shared_state.lock().last_monitor = Some(monitor.clone());
|
||||
}
|
||||
monitor
|
||||
})
|
||||
self.shared_state.lock().last_monitor.clone()
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
|
||||
@@ -848,44 +871,31 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn set_visible(&self, visible: bool) {
|
||||
let is_visible = self.shared_state.lock().is_visible;
|
||||
let mut shared_state = self.shared_state.lock();
|
||||
|
||||
if visible == is_visible {
|
||||
return;
|
||||
match (visible, shared_state.visibility) {
|
||||
(true, Visibility::Yes) | (true, Visibility::YesWait) | (false, Visibility::No) => {
|
||||
return
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match visible {
|
||||
true => unsafe {
|
||||
if visible {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
|
||||
self.xconn
|
||||
.flush_requests()
|
||||
.expect("Failed to call XMapRaised");
|
||||
|
||||
// Some X requests may generate an error if the window is not
|
||||
// visible, so we must wait until the window becomes visible.
|
||||
self.wait_for_visibility_notify();
|
||||
},
|
||||
false => unsafe {
|
||||
}
|
||||
self.xconn
|
||||
.flush_requests()
|
||||
.expect("Failed to call XMapRaised");
|
||||
shared_state.visibility = Visibility::YesWait;
|
||||
} else {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
|
||||
self.xconn
|
||||
.flush_requests()
|
||||
.expect("Failed to call XUnmapWindow");
|
||||
},
|
||||
}
|
||||
|
||||
self.shared_state.lock().is_visible = visible;
|
||||
}
|
||||
|
||||
fn wait_for_visibility_notify(&self) {
|
||||
unsafe {
|
||||
let mut event = MaybeUninit::uninit();
|
||||
|
||||
(self.xconn.xlib.XIfEvent)(
|
||||
self.xconn.display,
|
||||
event.as_mut_ptr(),
|
||||
Some(visibility_predicate),
|
||||
self.xwindow as _,
|
||||
);
|
||||
}
|
||||
self.xconn
|
||||
.flush_requests()
|
||||
.expect("Failed to call XUnmapWindow");
|
||||
shared_state.visibility = Visibility::No;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -982,17 +992,6 @@ impl UnownedWindow {
|
||||
self.logicalize_size(self.inner_size_physical())
|
||||
}
|
||||
|
||||
pub(crate) fn outer_size_physical(&self) -> (u32, u32) {
|
||||
let extents = self.shared_state.lock().frame_extents.clone();
|
||||
if let Some(extents) = extents {
|
||||
let (w, h) = self.inner_size_physical();
|
||||
extents.inner_size_to_outer(w, h)
|
||||
} else {
|
||||
self.update_cached_frame_extents();
|
||||
self.outer_size_physical()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn outer_size(&self) -> LogicalSize {
|
||||
let extents = self.shared_state.lock().frame_extents.clone();
|
||||
|
||||
@@ -275,7 +275,7 @@ impl AppState {
|
||||
HANDLER.set_in_callback(false);
|
||||
}
|
||||
if HANDLER.should_exit() {
|
||||
let _: () = unsafe { msg_send![NSApp(), stop: nil] };
|
||||
let _: () = unsafe { msg_send![NSApp(), terminate: nil] };
|
||||
return;
|
||||
}
|
||||
HANDLER.update_start_time();
|
||||
|
||||
@@ -208,7 +208,7 @@ pub fn scancode_to_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
|
||||
// While F1-F20 have scancodes we can match on, we have to check against UTF-16
|
||||
// constants for the rest.
|
||||
// https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ
|
||||
pub fn check_function_keys(string: &String) -> Option<VirtualKeyCode> {
|
||||
pub fn check_function_keys(string: &str) -> Option<VirtualKeyCode> {
|
||||
if let Some(ch) = string.encode_utf16().next() {
|
||||
return Some(match ch {
|
||||
0xf718 => VirtualKeyCode::F21,
|
||||
@@ -264,6 +264,7 @@ pub unsafe fn modifier_event(
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -132,7 +132,7 @@ impl<T> Proxy<T> {
|
||||
// process user events through the normal OS EventLoop mechanisms.
|
||||
let rl = CFRunLoopGetMain();
|
||||
let mut context: CFRunLoopSourceContext = mem::zeroed();
|
||||
context.perform = event_loop_proxy_handler;
|
||||
context.perform = Some(event_loop_proxy_handler);
|
||||
let source =
|
||||
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
@@ -142,8 +142,10 @@ impl<T> Proxy<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
self.sender.send(event).map_err(|_| EventLoopClosed)?;
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.sender
|
||||
.send(event)
|
||||
.map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
|
||||
unsafe {
|
||||
// let the main thread know there's a new event
|
||||
CFRunLoopSourceSignal(self.source);
|
||||
|
||||
@@ -248,7 +248,6 @@ impl MonitorHandle {
|
||||
assert!(!array.is_null(), "failed to get list of display modes");
|
||||
let array_count = CFArrayGetCount(array);
|
||||
let modes: Vec<_> = (0..array_count)
|
||||
.into_iter()
|
||||
.map(move |i| {
|
||||
let mode = CFArrayGetValueAtIndex(array, i) as *mut _;
|
||||
ffi::CGDisplayModeRetain(mode);
|
||||
|
||||
@@ -93,14 +93,14 @@ pub enum CFRunLoopTimerContext {}
|
||||
pub struct CFRunLoopSourceContext {
|
||||
pub version: CFIndex,
|
||||
pub info: *mut c_void,
|
||||
pub retain: extern "C" fn(*const c_void) -> *const c_void,
|
||||
pub release: extern "C" fn(*const c_void),
|
||||
pub copyDescription: extern "C" fn(*const c_void) -> CFStringRef,
|
||||
pub equal: extern "C" fn(*const c_void, *const c_void) -> ffi::Boolean,
|
||||
pub hash: extern "C" fn(*const c_void) -> CFHashCode,
|
||||
pub schedule: extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode),
|
||||
pub cancel: extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode),
|
||||
pub perform: extern "C" fn(*mut c_void),
|
||||
pub retain: Option<extern "C" fn(*const c_void) -> *const c_void>,
|
||||
pub release: Option<extern "C" fn(*const c_void)>,
|
||||
pub copyDescription: Option<extern "C" fn(*const c_void) -> CFStringRef>,
|
||||
pub equal: Option<extern "C" fn(*const c_void, *const c_void) -> ffi::Boolean>,
|
||||
pub hash: Option<extern "C" fn(*const c_void) -> CFHashCode>,
|
||||
pub schedule: Option<extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode)>,
|
||||
pub cancel: Option<extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode)>,
|
||||
pub perform: Option<extern "C" fn(*mut c_void)>,
|
||||
}
|
||||
|
||||
// begin is queued with the highest priority to ensure it is processed before other observers
|
||||
|
||||
@@ -452,7 +452,7 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
|
||||
//let event: id = msg_send![NSApp(), currentEvent];
|
||||
|
||||
let mut events = VecDeque::with_capacity(characters.len());
|
||||
for character in string.chars() {
|
||||
for character in string.chars().filter(|c| !is_corporate_character(*c)) {
|
||||
events.push_back(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
event: WindowEvent::ReceivedCharacter(character),
|
||||
@@ -484,7 +484,10 @@ extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
|
||||
} else {
|
||||
let raw_characters = state.raw_characters.take();
|
||||
if let Some(raw_characters) = raw_characters {
|
||||
for character in raw_characters.chars() {
|
||||
for character in raw_characters
|
||||
.chars()
|
||||
.filter(|c| !is_corporate_character(*c))
|
||||
{
|
||||
events.push_back(Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.ns_window)),
|
||||
event: WindowEvent::ReceivedCharacter(character),
|
||||
@@ -515,15 +518,27 @@ fn get_characters(event: id, ignore_modifiers: bool) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
// As defined in: https://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
|
||||
fn is_corporate_character(c: char) -> bool {
|
||||
match c {
|
||||
'\u{F700}'..='\u{F747}'
|
||||
| '\u{F802}'..='\u{F84F}'
|
||||
| '\u{F850}'
|
||||
| '\u{F85C}'
|
||||
| '\u{F85D}'
|
||||
| '\u{F85F}'
|
||||
| '\u{F860}'..='\u{F86B}'
|
||||
| '\u{F870}'..='\u{F8FF}' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieves a layout-independent keycode given an event.
|
||||
fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
|
||||
#[inline]
|
||||
fn get_code(ev: id, raw: bool) -> Option<VirtualKeyCode> {
|
||||
let characters = get_characters(ev, raw);
|
||||
characters
|
||||
.chars()
|
||||
.next()
|
||||
.map_or(None, |c| char_to_keycode(c))
|
||||
characters.chars().next().and_then(|c| char_to_keycode(c))
|
||||
}
|
||||
|
||||
// Cmd switches Roman letters for Dvorak-QWERTY layout, so we try modified characters first.
|
||||
@@ -567,6 +582,7 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -574,7 +590,7 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||
AppState::queue_event(window_event);
|
||||
// Emit `ReceivedCharacter` for key repeats
|
||||
if is_repeat && state.is_key_down {
|
||||
for character in characters.chars() {
|
||||
for character in characters.chars().filter(|c| !is_corporate_character(*c)) {
|
||||
AppState::queue_event(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ReceivedCharacter(character),
|
||||
@@ -618,6 +634,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -726,6 +743,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ impl<T: 'static> Proxy<T> {
|
||||
Proxy { runner }
|
||||
}
|
||||
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.runner.send_event(Event::UserEvent(event));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ impl<T> WindowTarget<T> {
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -83,6 +84,7 @@ impl<T> WindowTarget<T> {
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,9 @@ mod backend;
|
||||
#[path = "stdweb/mod.rs"]
|
||||
mod backend;
|
||||
|
||||
#[cfg(not(any(feature = "web-sys", feature = "stdweb")))]
|
||||
compile_error!("Please select a feature to build for web: `web-sys`, `stdweb`");
|
||||
|
||||
pub use self::device::Id as DeviceId;
|
||||
pub use self::error::OsError;
|
||||
pub use self::event_loop::{
|
||||
|
||||
@@ -39,7 +39,7 @@ unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option<c
|
||||
hkl,
|
||||
);
|
||||
if len >= 1 {
|
||||
char::decode_utf16(unicode_bytes.into_iter().cloned())
|
||||
char::decode_utf16(unicode_bytes.iter().cloned())
|
||||
.next()
|
||||
.and_then(|c| c.ok())
|
||||
} else {
|
||||
|
||||
@@ -742,13 +742,13 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
unsafe {
|
||||
if winuser::PostMessageW(self.target_window, *USER_EVENT_MSG_ID, 0, 0) != 0 {
|
||||
self.event_send.send(event).ok();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EventLoopClosed)
|
||||
Err(EventLoopClosed(event))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1112,7 +1112,7 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||
0
|
||||
}
|
||||
|
||||
winuser::WM_CHAR => {
|
||||
winuser::WM_CHAR | winuser::WM_SYSCHAR => {
|
||||
use crate::event::WindowEvent::ReceivedCharacter;
|
||||
use std::char;
|
||||
let is_high_surrogate = 0xD800 <= wparam && wparam <= 0xDBFF;
|
||||
@@ -1145,12 +1145,6 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||
0
|
||||
}
|
||||
|
||||
// Prevents default windows menu hotkeys playing unwanted
|
||||
// "ding" sounds. Alternatively could check for WM_SYSCOMMAND
|
||||
// with wparam being SC_KEYMENU, but this may prevent some
|
||||
// other unwanted default hotkeys as well.
|
||||
winuser::WM_SYSCHAR => 0,
|
||||
|
||||
winuser::WM_SYSCOMMAND => {
|
||||
if wparam == winuser::SC_SCREENSAVE {
|
||||
let window_state = subclass_input.window_state.lock();
|
||||
@@ -1283,6 +1277,7 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
// Windows doesn't emit a delete character by default, but in order to make it
|
||||
@@ -1311,6 +1306,7 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -202,7 +202,10 @@ impl Window {
|
||||
y as c_int,
|
||||
0,
|
||||
0,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER | winuser::SWP_NOSIZE,
|
||||
winuser::SWP_ASYNCWINDOWPOS
|
||||
| winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOSIZE
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
winuser::UpdateWindow(self.window.0);
|
||||
}
|
||||
@@ -615,7 +618,9 @@ impl Window {
|
||||
client_rect.top,
|
||||
client_rect.right - client_rect.left,
|
||||
client_rect.bottom - client_rect.top,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOZORDER,
|
||||
winuser::SWP_ASYNCWINDOWPOS
|
||||
| winuser::SWP_NOZORDER
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
winuser::UpdateWindow(window.0);
|
||||
}
|
||||
|
||||
@@ -255,7 +255,10 @@ impl WindowFlags {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOMOVE | winuser::SWP_NOSIZE,
|
||||
winuser::SWP_ASYNCWINDOWPOS
|
||||
| winuser::SWP_NOMOVE
|
||||
| winuser::SWP_NOSIZE
|
||||
| winuser::SWP_NOACTIVATE,
|
||||
);
|
||||
winuser::UpdateWindow(window);
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_inner_size`] for details.
|
||||
///
|
||||
/// [`Window::set_inner_size`]: struct.Window.html#method.set_inner_size
|
||||
/// [`Window::set_inner_size`]: crate::window::Window::set_inner_size
|
||||
#[inline]
|
||||
pub fn with_inner_size(mut self, size: LogicalSize) -> Self {
|
||||
self.window.inner_size = Some(size);
|
||||
@@ -206,7 +206,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_min_inner_size`] for details.
|
||||
///
|
||||
/// [`Window::set_min_inner_size`]: struct.Window.html#method.set_min_inner_size
|
||||
/// [`Window::set_min_inner_size`]: crate::window::Window::set_min_inner_size
|
||||
#[inline]
|
||||
pub fn with_min_inner_size(mut self, min_size: LogicalSize) -> Self {
|
||||
self.window.min_inner_size = Some(min_size);
|
||||
@@ -217,7 +217,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_max_inner_size`] for details.
|
||||
///
|
||||
/// [`Window::set_max_inner_size`]: struct.Window.html#method.set_max_inner_size
|
||||
/// [`Window::set_max_inner_size`]: crate::window::Window::set_max_inner_size
|
||||
#[inline]
|
||||
pub fn with_max_inner_size(mut self, max_size: LogicalSize) -> Self {
|
||||
self.window.max_inner_size = Some(max_size);
|
||||
@@ -228,7 +228,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_resizable`] for details.
|
||||
///
|
||||
/// [`Window::set_resizable`]: struct.Window.html#method.set_resizable
|
||||
/// [`Window::set_resizable`]: crate::window::Window::set_resizable
|
||||
#[inline]
|
||||
pub fn with_resizable(mut self, resizable: bool) -> Self {
|
||||
self.window.resizable = resizable;
|
||||
@@ -239,7 +239,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_title`] for details.
|
||||
///
|
||||
/// [`Window::set_title`]: struct.Window.html#method.set_title
|
||||
/// [`Window::set_title`]: crate::window::Window::set_title
|
||||
#[inline]
|
||||
pub fn with_title<T: Into<String>>(mut self, title: T) -> Self {
|
||||
self.window.title = title.into();
|
||||
@@ -250,7 +250,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_fullscreen`] for details.
|
||||
///
|
||||
/// [`Window::set_fullscreen`]: struct.Window.html#method.set_fullscreen
|
||||
/// [`Window::set_fullscreen`]: crate::window::Window::set_fullscreen
|
||||
#[inline]
|
||||
pub fn with_fullscreen(mut self, monitor: Option<Fullscreen>) -> Self {
|
||||
self.window.fullscreen = monitor;
|
||||
@@ -261,7 +261,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_maximized`] for details.
|
||||
///
|
||||
/// [`Window::set_maximized`]: struct.Window.html#method.set_maximized
|
||||
/// [`Window::set_maximized`]: crate::window::Window::set_maximized
|
||||
#[inline]
|
||||
pub fn with_maximized(mut self, maximized: bool) -> Self {
|
||||
self.window.maximized = maximized;
|
||||
@@ -272,7 +272,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_visible`] for details.
|
||||
///
|
||||
/// [`Window::set_visible`]: struct.Window.html#method.set_visible
|
||||
/// [`Window::set_visible`]: crate::window::Window::set_visible
|
||||
#[inline]
|
||||
pub fn with_visible(mut self, visible: bool) -> Self {
|
||||
self.window.visible = visible;
|
||||
@@ -290,7 +290,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_decorations`] for details.
|
||||
///
|
||||
/// [`Window::set_decorations`]: struct.Window.html#method.set_decorations
|
||||
/// [`Window::set_decorations`]: crate::window::Window::set_decorations
|
||||
#[inline]
|
||||
pub fn with_decorations(mut self, decorations: bool) -> Self {
|
||||
self.window.decorations = decorations;
|
||||
@@ -301,7 +301,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_always_on_top`] for details.
|
||||
///
|
||||
/// [`Window::set_always_on_top`]: struct.Window.html#method.set_always_on_top
|
||||
/// [`Window::set_always_on_top`]: crate::window::Window::set_always_on_top
|
||||
#[inline]
|
||||
pub fn with_always_on_top(mut self, always_on_top: bool) -> Self {
|
||||
self.window.always_on_top = always_on_top;
|
||||
@@ -312,7 +312,7 @@ impl WindowBuilder {
|
||||
///
|
||||
/// See [`Window::set_window_icon`] for details.
|
||||
///
|
||||
/// [`Window::set_window_icon`]: struct.Window.html#method.set_window_icon
|
||||
/// [`Window::set_window_icon`]: crate::window::Window::set_window_icon
|
||||
#[inline]
|
||||
pub fn with_window_icon(mut self, window_icon: Option<Icon>) -> Self {
|
||||
self.window.window_icon = window_icon;
|
||||
@@ -353,7 +353,7 @@ impl Window {
|
||||
/// - **Web**: The window is created but not inserted into the web page automatically. Please
|
||||
/// see the web platform module for more information.
|
||||
///
|
||||
/// [`WindowBuilder::new().build(event_loop)`]: struct.WindowBuilder.html#method.build
|
||||
/// [`WindowBuilder::new().build(event_loop)`]: crate::window::WindowBuilder::build
|
||||
#[inline]
|
||||
pub fn new<T: 'static>(event_loop: &EventLoopWindowTarget<T>) -> Result<Window, OsError> {
|
||||
let builder = WindowBuilder::new();
|
||||
@@ -368,7 +368,7 @@ impl Window {
|
||||
|
||||
/// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||
///
|
||||
/// See the [`dpi`](../dpi/index.html) module for more information.
|
||||
/// See the [`dpi`](crate::dpi) module for more information.
|
||||
///
|
||||
/// Note that this value can change depending on user action (for example if the window is
|
||||
/// moved to another screen); as such, tracking `WindowEvent::HiDpiFactorChanged` events is
|
||||
@@ -595,16 +595,13 @@ impl Window {
|
||||
///
|
||||
/// `Fullscreen::Borderless` provides a borderless fullscreen window on a
|
||||
/// separate space. This is the idiomatic way for fullscreen games to work
|
||||
/// on macOS. See [`WindowExtMacOs::set_simple_fullscreen`][simple] if
|
||||
/// on macOS. See `WindowExtMacOs::set_simple_fullscreen` if
|
||||
/// separate spaces are not preferred.
|
||||
///
|
||||
/// The dock and the menu bar are always disabled in fullscreen mode.
|
||||
/// - **iOS:** Can only be called on the main thread.
|
||||
/// - **Wayland:** Does not support exclusive fullscreen mode.
|
||||
/// - **Windows:** Screen saver is disabled in fullscreen mode.
|
||||
///
|
||||
/// [simple]:
|
||||
/// ../platform/macos/trait.WindowExtMacOS.html#tymethod.set_simple_fullscreen
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
|
||||
self.window.set_fullscreen(fullscreen)
|
||||
|
||||
Reference in New Issue
Block a user