Compare commits

..

26 Commits

Author SHA1 Message Date
Kirill Chibisov
adfa5bd275 Release 0.27.3 version 2022-09-11 19:59:13 +03:00
Kirill Chibisov
66319c571c On X11 query for XIM styles before creating IME
Fixes #2448.
2022-09-11 19:59:13 +03:00
Kirill Chibisov
f1470d1ab7 Send empty Ime::Preedit before the Ime::Commit
This should help downstream to automatically clear it.
2022-09-11 19:59:13 +03:00
Kirill Chibisov
79dc6bf4ab Remove automatic publish script
This script is confusing and provides no value especially
with release branches and patch fixes.
2022-09-11 19:59:13 +03:00
Kirill Chibisov
ad0520e935 Specify minimum supported version for RWH 0.4
Winit uses raw-window-handle of version 0.4.3,
but only 0.4.0 was specified.
2022-09-11 19:59:13 +03:00
Weng Xuetian
156fa375ea Clear preedit if there is no pending preedit on Wayland
Fixes #2478.
2022-09-11 19:59:13 +03:00
Marijn Suijten
2a2733b2d1 Revert "ci: manually point ANDROID_NDK_ROOT to latest supplied version"
This reverts commit 4895a29e92.

GitHub Actions' runner-images readded this environment variable on my
request [1] as it wasn't strictly related to the deprecated and removed
`ndk-bundle` NDK release.  Back out of the workaround to keep CI scripts
tidy.

[1]: https://github.com/actions/runner-images/issues/5879#issuecomment-1197811704
2022-09-11 19:59:13 +03:00
Mads Marquart
7af1163ee7 iOS: Fix a few instances of UB (#2428)
* Fix iOS 32-bit

* Fix a few invalid message sends on iOS
2022-09-11 19:59:13 +03:00
ajtribick
229dbffa17 Windows: Update handling of system keypresses (#2445)
- Pass WM_SYSKEYDOWN to DefWindowProc
- Avoid intercepting WM_SYSCHAR to allow ALT+Space to work: removes ReceivedCharacter events for alt+keypress
- Intercept WM_MENUCHAR to disable bell sound
2022-09-11 19:59:13 +03:00
daxpedda
a5457b24c2 Document WindowEvent::Moved OS support (#2442) 2022-09-11 19:59:13 +03:00
ajtribick
5645bb1459 Disable default features in simple_logger
This fix CI building due to implicit rust version
bump in examples.
2022-09-11 19:59:13 +03:00
Sludge
77cd021c02 Document WindowEvent::Moved as unsupported on Wayland 2022-09-11 19:59:13 +03:00
Markus Siglreithmaier
2d732069a9 On Windows, improve support for undecorated windows (#2419) 2022-09-11 19:59:13 +03:00
Kirill Chibisov
2e4338bb8d Release 0.27.2 version 2022-08-12 14:39:44 +04:00
Kirill Chibisov
ec2888b8b7 On Wayland, fix Window::request_redraw being delayed
On Waylnad when asking for redraw before `MainEventsCleared`
would result for redraw being send on the next event loop tick,
which is not expectable given that it must be delivered on the same
event loop tick.
2022-08-12 11:54:02 +04:00
Kirill Chibisov
fa83bace12 Remove redundant steps from CI
Tests are already building the entire crate, so no need for a
separate builds slowing down the CI.
2022-08-11 19:31:11 +04:00
Kirill Chibisov
ee7dc48e3b Fix missleading breaking change on Windows
The applications should not rely on not-implemented behavior and
should use the right functions for that.
2022-08-11 19:07:40 +04:00
Robert Bragg
11d4a301e4 Implement version 0.4 of the HasRawWindowHandle trait
This makes Winit 0.27 compatible with crates like Wgpu 0.13 that are
using the raw_window_handle v0.4 crate and aren't able to upgrade to 0.5
until they do a new release (since it requires a semver change).

The change is intended to be self-contained (instead of pushing
the details into all the platform_impl backends) since this is only
intended to be a temporary trait implementation for backwards
compatibility that will likely be removed before the next Winit release.

Fixes #2415.
2022-08-11 18:33:02 +04:00
Mads Marquart
ad41eaf151 Add CODEOWNERS file (#2420)
* Add CODEOWNERS file

This makes it very clear when you're stepping down from the post as a maintainer, and makes it clear for users who is expected to review their PR

* Fix grammar

* Make @kchibisov receive pings for the X11 platform

* Fix typo

Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
2022-08-11 16:13:56 +02:00
ajtribick
9b71df9f97 On Windows, add opt-in function for device events (#2409) 2022-08-11 15:17:46 +02:00
Mads Marquart
b1c9e4a6fa Fix tracking of phase changes for mousewheel on trackpad (#2158) 2022-08-10 18:28:19 +02:00
Anton Bulakh
cdbaf4816a On X11, fix window hints not persisting
This commit fixes the issue with min, max, and resize increments
not persisting across the dpi changes.
2022-08-08 18:27:11 +04:00
Amr Bashir
6b7ceedc91 Windows: respect min/max sizes when creating the window (#2393) 2022-08-04 23:03:55 +02:00
Kirill Chibisov
c53a574bff Release 0.27.1 version 2022-07-30 19:33:23 +03:00
Kirill Chibisov
95246d81c1 On X11, fix crash when can't disable IME
Fixes #2402.
2022-07-30 17:15:57 +03:00
Kirill Chibisov
bf537009d9 Explicitly specify minimum supported rust version
This should help with distributing apps using winit.

Fixes #1075.
2022-07-29 14:39:41 +03:00
42 changed files with 978 additions and 510 deletions

35
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,35 @@
# Core maintainers:
# - @msiglreith
# - @kchibisov
# - @madsmtm
# - @maroider
# Android
/src/platform/android.rs @msiglreith
/src/platform_impl/android @msiglreith
# iOS
/src/platform/ios.rs @francesca64
/src/platform_impl/ios @francesca64
# Unix in general
/src/platform/unix.rs @kchibisov
/src/platform_impl/linux/mod.rs @kchibisov
# Wayland
/src/platform_impl/linux/wayland @kchibisov
# X11
/src/platform_impl/linux/x11 @kchibisov
# macOS
/src/platform/macos.rs @madsmtm
/src/platform_impl/macos @madsmtm
# Web (no maintainer)
/src/platform/web.rs
/src/platform_impl/web
# Windows
/src/platform/windows.rs @msiglreith
/src/platform_impl/windows @msiglreith

View File

@@ -22,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
rust_version: [stable, nightly]
rust_version: [1.57.0, stable, nightly]
platform:
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
- { target: x86_64-pc-windows-msvc, os: windows-latest, }
@@ -67,14 +67,10 @@ jobs:
targets: ${{ matrix.platform.target }}
components: clippy
- name: Setup NDK path
shell: bash
# "Temporary" workaround until https://github.com/actions/virtual-environments/issues/5879#issuecomment-1195156618
# gets looked into.
run: echo "ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV
- name: Install Linux dependencies
if: (matrix.platform.os == 'ubuntu-latest')
run: sudo apt-get update && sudo apt-get install pkg-config cmake libfreetype6-dev libfontconfig1-dev
- name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install g++-multilib gcc-multilib libfreetype6-dev:i386 libfontconfig1-dev:i386
@@ -86,10 +82,6 @@ jobs:
shell: bash
run: cargo $CMD doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES --document-private-items
- name: Build
shell: bash
run: cargo $CMD build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
- name: Build tests
shell: bash
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
@@ -103,13 +95,9 @@ jobs:
- name: Lint with clippy
shell: bash
if: (matrix.rust_version != 'nightly') && !contains(matrix.platform.options, '--no-default-features')
if: (matrix.rust_version == '1.57.0') && !contains(matrix.platform.options, '--no-default-features')
run: cargo clippy --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings
- name: Build with serde enabled
shell: bash
run: cargo $CMD build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
- name: Build tests with serde enabled
shell: bash
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES

View File

@@ -1,18 +0,0 @@
name: Publish
on:
push:
tags:
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
jobs:
Publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: hecrj/setup-rust-action@v1
with:
rust-version: stable
components: rustfmt
- name: Publish to crates.io
run: cargo publish --token ${{ secrets.cratesio_token }}

View File

@@ -8,6 +8,30 @@ And please only add new entries to the top of this list, right below the `# Unre
# Unreleased
# 0.27.3 (2022-9-10)
- On Windows, added `WindowExtWindows::set_undecorated_shadow` and `WindowBuilderExtWindows::with_undecorated_shadow` to draw the drop shadow behind a borderless window.
- On Windows, fixed default window features (ie snap, animations, shake, etc.) when decorations are disabled.
- On Windows, fixed ALT+Space shortcut to open window menu.
- On Wayland, fixed `Ime::Preedit` not being sent on IME reset.
- Fixed unbound version specified for `raw-window-handle` leading to compilation failures.
- Empty `Ime::Preedit` event will be sent before `Ime::Commit` to help clearing preedit.
- On X11, fixed IME context picking by querying for supported styles beforehand.
# 0.27.2 (2022-8-12)
- On macOS, fixed touch phase reporting when scrolling.
- On X11, fix min, max and resize increment hints not persisting for resizable windows (e.g. on DPI change).
- On Windows, respect min/max inner sizes when creating the window.
- For backwards compatibility, `Window` now (additionally) implements the old version (`0.4`) of the `HasRawWindowHandle` trait
- On Windows, added support for `EventLoopWindowTarget::set_device_event_filter`.
- On Wayland, fix user requested `WindowEvent::RedrawRequested` being delayed by a frame.
# 0.27.1 (2022-07-30)
- The minimum supported Rust version was lowered to `1.57.0` and now explicitly tested.
- On X11, fix crash on start due to inability to create an IME context without any preedit.
# 0.27.0 (2022-07-26)
- On Windows, fix hiding a maximized window.

View File

@@ -20,6 +20,7 @@ your description of the issue as detailed as possible:
When making a code contribution to winit, before opening your pull request, please make sure that:
- your patch builds with Winit's minimal supported rust version - Rust 1.57.0.
- you tested your modifications on all the platforms impacted, or if not possible detail which platforms
were not tested, and what should be tested, so that a maintainer or another contributor can test them
- you updated any relevant documentation in winit
@@ -43,10 +44,9 @@ Once your PR is deemed ready, the merging maintainer will take care of resolving
## Maintainers & Testers
The current [list of testers and contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors)
can be found on the Wiki.
The current maintainers are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
If you are interested in contributing or testing on a platform, please add yourself to that table!
If you are interested in being pinged when testing is needed for a certain platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
## Making a new release

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.27.0"
version = "0.27.3"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
edition = "2021"
@@ -10,6 +10,7 @@ readme = "README.md"
repository = "https://github.com/rust-windowing/winit"
documentation = "https://docs.rs/winit"
categories = ["gui"]
rust-version = "1.57.0"
[package.metadata.docs.rs]
features = ["serde"]
@@ -45,13 +46,14 @@ instant = { version = "0.1", features = ["wasm-bindgen"] }
once_cell = "1.12"
log = "0.4"
serde = { version = "1", optional = true, features = ["serde_derive"] }
raw-window-handle = "0.5.0"
raw_window_handle = { package = "raw-window-handle", version = "0.5" }
raw_window_handle_04 = { package = "raw-window-handle", version = "0.4.3" }
bitflags = "1"
mint = { version = "0.5.6", optional = true }
[dev-dependencies]
image = { version = "0.24.0", default-features = false, features = ["png"] }
simple_logger = "2.1.0"
simple_logger = { version = "2.1.0", default_features = false }
[target.'cfg(target_os = "android")'.dependencies]
# Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995

View File

@@ -15,9 +15,12 @@ future endeavors:
vastly more sustainable era of winit.
* [@goddessfreya]: For selflessly taking over maintainership of glutin, and her
stellar dedication to improving both winit and glutin.
* [@ArturKovacs]: For consistently maintaining the macOS backend, and his
immense involvement in designing and implementing the new keyboard API.
[@tomaka]: https://github.com/tomaka
[@vberger]: https://github.com/vberger
[@francesca64]: https://github.com/francesca64
[@Osspial]: https://github.com/Osspial
[@goddessfreya]: https://github.com/goddessfreya
[@ArturKovacs]: https://github.com/ArturKovacs

View File

@@ -6,7 +6,7 @@
```toml
[dependencies]
winit = "0.27.0"
winit = "0.27.3"
```
## [Documentation](https://docs.rs/winit)

View File

@@ -59,12 +59,12 @@ fn main() {
}
VirtualKeyCode::F => {
let fullscreen = Some(Fullscreen::Exclusive(mode.clone()));
println!("Setting mode: {fullscreen:?}");
println!("Setting mode: {:?}", fullscreen);
window.set_fullscreen(fullscreen);
}
VirtualKeyCode::B => {
let fullscreen = Some(Fullscreen::Borderless(Some(monitor.clone())));
println!("Setting mode: {fullscreen:?}");
println!("Setting mode: {:?}", fullscreen);
window.set_fullscreen(fullscreen);
}
VirtualKeyCode::S => {

View File

@@ -511,6 +511,29 @@ impl Size {
Size::Logical(size) => size.to_physical(scale_factor),
}
}
pub fn clamp<S: Into<Size>>(input: S, min: S, max: S, scale_factor: f64) -> Size {
let (input, min, max) = (
input.into().to_physical::<f64>(scale_factor),
min.into().to_physical::<f64>(scale_factor),
max.into().to_physical::<f64>(scale_factor),
);
let clamp = |input: f64, min: f64, max: f64| {
if input < min {
min
} else if input > max {
max
} else {
input
}
};
let width = clamp(input.width, min.width, max.width);
let height = clamp(input.height, min.height, max.height);
PhysicalSize::new(width, height).into()
}
}
impl<P: Pixel> From<PhysicalSize<P>> for Size {

View File

@@ -321,6 +321,10 @@ pub enum WindowEvent<'a> {
Resized(PhysicalSize<u32>),
/// The position of the window has changed. Contains the window's new position.
///
/// ## Platform-specific
///
/// - **iOS / Android / Web / Wayland:** Unsupported.
Moved(PhysicalPosition<i32>),
/// The window has been requested to close.
@@ -773,8 +777,9 @@ pub struct KeyboardInput {
/// the character you want to apply the accent to. This will generate the following event sequence:
/// ```ignore
/// // Press "`" key
/// Ime::Preedit("`", Some(0), Some(0))
/// Ime::Preedit("`", Some((0, 0)))
/// // Press "E" key
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
/// Ime::Commit("é")
/// ```
///
@@ -785,14 +790,15 @@ pub struct KeyboardInput {
/// sequence could be obtained:
/// ```ignore
/// // Press "A" key
/// Ime::Preedit("a", Some(1), Some(1))
/// Ime::Preedit("a", Some((1, 1)))
/// // Press "B" key
/// Ime::Preedit("a b", Some(3), Some(3))
/// Ime::Preedit("a b", Some((3, 3)))
/// // Press left arrow key
/// Ime::Preedit("a b", Some(1), Some(1))
/// Ime::Preedit("a b", Some((1, 1)))
/// // Press space key
/// Ime::Preedit("啊b", Some(3), Some(3))
/// Ime::Preedit("啊b", Some((3, 3)))
/// // Press space key
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
/// Ime::Commit("啊不")
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -808,20 +814,21 @@ pub enum Ime {
/// Notifies when a new composing text should be set at the cursor position.
///
/// The value represents a pair of the preedit string and the cursor begin position and end
/// position. When it's `None`, the cursor should be hidden.
/// position. When it's `None`, the cursor should be hidden. When `String` is an empty string
/// this indicates that preedit was cleared.
///
/// The cursor position is byte-wise indexed.
Preedit(String, Option<(usize, usize)>),
/// Notifies when text should be inserted into the editor widget.
///
/// Any pending [`Preedit`](Self::Preedit) must be cleared.
/// Right before this event winit will send empty [`Self::Preedit`] event.
Commit(String),
/// Notifies when the IME was disabled.
///
/// After receiving this event you won't get any more [`Preedit`](Self::Preedit) or
/// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You can
/// [`Commit`](Self::Commit) events until the next [`Enabled`](Self::Enabled) event. You should
/// also stop issuing IME related requests like [`Window::set_ime_position`] and clear pending
/// preedit text.
Disabled,

View File

@@ -323,7 +323,7 @@ impl<T> EventLoopWindowTarget<T> {
///
/// ## Platform-specific
///
/// - **Wayland / Windows / macOS / iOS / Android / Web:** Unsupported.
/// - **Wayland / macOS / iOS / Android / Web:** Unsupported.
///
/// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn set_device_event_filter(&self, _filter: DeviceEventFilter) {
@@ -332,7 +332,8 @@ impl<T> EventLoopWindowTarget<T> {
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
target_os = "openbsd",
target_os = "windows"
))]
self.p.set_device_event_filter(_filter);
}

View File

@@ -299,10 +299,11 @@ impl WindowExtUnix for Window {
#[inline]
#[cfg(feature = "wayland")]
fn wayland_set_csd_theme(&self, theme: Theme) {
#[allow(clippy::single_match)]
match self.window {
LinuxWindow::Wayland(ref w) => w.set_csd_theme(theme),
#[cfg(feature = "x11")]
_ => {}
_ => (),
}
}

View File

@@ -143,6 +143,11 @@ pub trait WindowExtWindows {
/// Whether to show or hide the window icon in the taskbar.
fn set_skip_taskbar(&self, skip: bool);
/// Shows or hides the background drop shadow for undecorated windows.
///
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn set_undecorated_shadow(&self, shadow: bool);
}
impl WindowExtWindows for Window {
@@ -175,6 +180,11 @@ impl WindowExtWindows for Window {
fn set_skip_taskbar(&self, skip: bool) {
self.window.set_skip_taskbar(skip)
}
#[inline]
fn set_undecorated_shadow(&self, shadow: bool) {
self.window.set_undecorated_shadow(shadow)
}
}
/// Additional methods on `WindowBuilder` that are specific to Windows.
@@ -229,6 +239,12 @@ pub trait WindowBuilderExtWindows {
/// Whether show or hide the window icon in the taskbar.
fn with_skip_taskbar(self, skip: bool) -> WindowBuilder;
/// Shows or hides the background drop shadow for undecorated windows.
///
/// The shadow is hidden by default.
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
fn with_undecorated_shadow(self, shadow: bool) -> WindowBuilder;
}
impl WindowBuilderExtWindows for WindowBuilder {
@@ -279,6 +295,12 @@ impl WindowBuilderExtWindows for WindowBuilder {
self.platform_specific.skip_taskbar = skip;
self
}
#[inline]
fn with_undecorated_shadow(mut self, shadow: bool) -> WindowBuilder {
self.platform_specific.decoration_shadow = shadow;
self
}
}
/// Additional methods on `MonitorHandle` that are specific to Windows.

View File

@@ -793,7 +793,7 @@ pub unsafe fn handle_main_events_cleared() {
return;
}
match this.state_mut() {
&mut AppStateImpl::ProcessingEvents { .. } => {}
AppStateImpl::ProcessingEvents { .. } => {}
_ => bug!("`ProcessingRedraws` happened unexpectedly"),
};
drop(this);

View File

@@ -7,6 +7,7 @@ use std::{
sync::mpsc::{self, Receiver, Sender},
};
use objc::runtime::Object;
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
use crate::{
@@ -113,7 +114,7 @@ impl<T: 'static> EventLoop<T> {
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
let application: *mut Object = msg_send![class!(UIApplication), sharedApplication];
assert_eq!(
application,
ptr::null_mut(),

View File

@@ -374,7 +374,7 @@ pub trait NSStringRust: Sized {
impl NSStringRust for id {
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
msg_send![self, initWithUTF8String: c_string as id]
msg_send![self, initWithUTF8String: c_string]
}
unsafe fn stringByAppendingString_(self, other: id) -> id {

View File

@@ -177,7 +177,7 @@ impl MonitorHandle {
pub fn retained_new(uiscreen: id) -> MonitorHandle {
unsafe {
assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS");
let _: () = msg_send![uiscreen, retain];
let _: id = msg_send![uiscreen, retain];
}
MonitorHandle {
inner: Inner { uiscreen },

View File

@@ -262,7 +262,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
let scale_factor: CGFloat = msg_send![object, contentScaleFactor];
PhysicalPosition::from_logical::<(f64, f64), f64>(
(logical_location.x as _, logical_location.y as _),
scale_factor,
scale_factor as f64,
)
};
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {

View File

@@ -411,8 +411,8 @@ impl Window {
let frame = match window_attributes.inner_size {
Some(dim) => {
let scale_factor = msg_send![screen, scale];
let size = dim.to_logical::<f64>(scale_factor);
let scale_factor: CGFloat = msg_send![screen, scale];
let size = dim.to_logical::<f64>(scale_factor as f64);
CGRect {
origin: screen_bounds.origin,
size: CGSize {
@@ -427,8 +427,8 @@ impl Window {
let view = view::create_view(&window_attributes, &platform_attributes, frame);
let gl_or_metal_backed = {
let view_class: id = msg_send![view, class];
let layer_class: id = msg_send![view_class, layerClass];
let view_class: *const Class = msg_send![view, class];
let layer_class: *const Class = msg_send![view_class, layerClass];
let is_metal: BOOL =
msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];

View File

@@ -587,7 +587,8 @@ impl Window {
/// Hooks for X11 errors.
#[cfg(feature = "x11")]
pub(crate) static mut XLIB_ERROR_HOOKS: Mutex<Vec<XlibErrorHook>> = Mutex::new(Vec::new());
pub(crate) static mut XLIB_ERROR_HOOKS: Lazy<Mutex<Vec<XlibErrorHook>>> =
Lazy::new(|| Mutex::new(Vec::new()));
#[cfg(feature = "x11")]
unsafe extern "C" fn x_error_callback(
@@ -633,7 +634,7 @@ unsafe extern "C" fn x_error_callback(
pub enum EventLoop<T: 'static> {
#[cfg(feature = "wayland")]
Wayland(wayland::EventLoop<T>),
Wayland(Box<wayland::EventLoop<T>>),
#[cfg(feature = "x11")]
X(x11::EventLoop<T>),
}
@@ -723,7 +724,7 @@ impl<T: 'static> EventLoop<T> {
#[cfg(feature = "wayland")]
fn new_wayland_any_thread() -> Result<EventLoop<T>, Box<dyn Error>> {
wayland::EventLoop::new().map(EventLoop::Wayland)
wayland::EventLoop::new().map(|evlp| EventLoop::Wayland(Box::new(evlp)))
}
#[cfg(feature = "x11")]

View File

@@ -2,6 +2,7 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::error::Error;
use std::io::Result as IOResult;
use std::mem;
use std::process;
use std::rc::Rc;
use std::time::{Duration, Instant};
@@ -26,7 +27,7 @@ use crate::platform_impl::EventLoopWindowTarget as PlatformEventLoopWindowTarget
use super::env::{WindowingFeatures, WinitEnv};
use super::output::OutputManager;
use super::seat::SeatManager;
use super::window::shim::{self, WindowUpdate};
use super::window::shim::{self, WindowCompositorUpdate, WindowUserRequest};
use super::{DeviceId, WindowId};
mod proxy;
@@ -82,6 +83,9 @@ impl<T> EventLoopWindowTarget<T> {
}
pub struct EventLoop<T: 'static> {
/// Dispatcher of Wayland events.
pub wayland_dispatcher: WinitDispatcher,
/// Event loop.
event_loop: calloop::EventLoop<'static, WinitState>,
@@ -94,9 +98,6 @@ pub struct EventLoop<T: 'static> {
/// Sender of user events.
user_events_sender: calloop::channel::Sender<T>,
/// Dispatcher of Wayland events.
pub wayland_dispatcher: WinitDispatcher,
/// Window target.
window_target: RootEventLoopWindowTarget<T>,
@@ -164,17 +165,19 @@ impl<T: 'static> EventLoop<T> {
let (event_loop_awakener, event_loop_awakener_source) = calloop::ping::make_ping()?;
// Handler of window requests.
event_loop.handle().insert_source(
event_loop_awakener_source,
move |_, _, winit_state| {
shim::handle_window_requests(winit_state);
},
)?;
event_loop
.handle()
.insert_source(event_loop_awakener_source, move |_, _, state| {
// Drain events here as well to account for application doing batch event processing
// on RedrawEventsCleared.
shim::handle_window_requests(state);
})?;
let event_loop_handle = event_loop.handle();
let window_map = HashMap::new();
let event_sink = EventSink::new();
let window_updates = HashMap::new();
let window_user_requests = HashMap::new();
let window_compositor_updates = HashMap::new();
// Create event loop window target.
let event_loop_window_target = EventLoopWindowTarget {
@@ -183,7 +186,8 @@ impl<T: 'static> EventLoop<T> {
state: RefCell::new(WinitState {
window_map,
event_sink,
window_updates,
window_user_requests,
window_compositor_updates,
}),
event_loop_handle,
output_manager,
@@ -236,7 +240,8 @@ impl<T: 'static> EventLoop<T> {
// applications don't themselves have a formal suspend/resume lifecycle.
callback(Event::Resumed, &self.window_target, &mut control_flow);
let mut window_updates: Vec<(WindowId, WindowUpdate)> = Vec::new();
let mut window_compositor_updates: Vec<(WindowId, WindowCompositorUpdate)> = Vec::new();
let mut window_user_requests: Vec<(WindowId, WindowUserRequest)> = Vec::new();
let mut event_sink_back_buffer = Vec::new();
// NOTE We break on errors from dispatches, since if we've got protocol error
@@ -357,25 +362,26 @@ impl<T: 'static> EventLoop<T> {
);
}
// Process 'new' pending updates.
// Process 'new' pending updates from compositor.
self.with_state(|state| {
window_updates.clear();
window_updates.extend(
window_compositor_updates.clear();
window_compositor_updates.extend(
state
.window_updates
.window_compositor_updates
.iter_mut()
.map(|(wid, window_update)| (*wid, window_update.take())),
.map(|(wid, window_update)| (*wid, mem::take(window_update))),
);
});
for (window_id, window_update) in window_updates.iter_mut() {
if let Some(scale_factor) = window_update.scale_factor.map(|f| f as f64) {
for (window_id, window_compositor_update) in window_compositor_updates.iter_mut() {
if let Some(scale_factor) = window_compositor_update.scale_factor.map(|f| f as f64)
{
let mut physical_size = self.with_state(|state| {
let window_handle = state.window_map.get(window_id).unwrap();
let mut size = window_handle.size.lock().unwrap();
// Update the new logical size if it was changed.
let window_size = window_update.size.unwrap_or(*size);
let window_size = window_compositor_update.size.unwrap_or(*size);
*size = window_size;
window_size.to_physical(scale_factor)
@@ -397,26 +403,27 @@ impl<T: 'static> EventLoop<T> {
// We don't update size on a window handle since we'll do that later
// when handling size update.
let new_logical_size = physical_size.to_logical(scale_factor);
window_update.size = Some(new_logical_size);
window_compositor_update.size = Some(new_logical_size);
}
if let Some(size) = window_update.size.take() {
if let Some(size) = window_compositor_update.size.take() {
let physical_size = self.with_state(|state| {
let window_handle = state.window_map.get_mut(window_id).unwrap();
let mut window_size = window_handle.size.lock().unwrap();
// Always issue resize event on scale factor change.
let physical_size =
if window_update.scale_factor.is_none() && *window_size == size {
// The size hasn't changed, don't inform downstream about that.
None
} else {
*window_size = size;
let scale_factor =
sctk::get_surface_scale_factor(window_handle.window.surface());
let physical_size = size.to_physical(scale_factor as f64);
Some(physical_size)
};
let physical_size = if window_compositor_update.scale_factor.is_none()
&& *window_size == size
{
// The size hasn't changed, don't inform downstream about that.
None
} else {
*window_size = size;
let scale_factor =
sctk::get_surface_scale_factor(window_handle.window.surface());
let physical_size = size.to_physical(scale_factor as f64);
Some(physical_size)
};
// We still perform all of those resize related logic even if the size
// hasn't changed, since GNOME relies on `set_geometry` calls after
@@ -425,7 +432,11 @@ impl<T: 'static> EventLoop<T> {
window_handle.window.refresh();
// Mark that refresh isn't required, since we've done it right now.
window_update.refresh_frame = false;
state
.window_user_requests
.get_mut(window_id)
.unwrap()
.refresh_frame = false;
physical_size
});
@@ -443,7 +454,8 @@ impl<T: 'static> EventLoop<T> {
}
}
if window_update.close_window {
// If the close is requested, send it here.
if window_compositor_update.close_window {
sticky_exit_callback(
Event::WindowEvent {
window_id: crate::window::WindowId(*window_id),
@@ -480,21 +492,40 @@ impl<T: 'static> EventLoop<T> {
&mut callback,
);
// Apply user requests, so every event required resize and latter surface commit will
// be applied right before drawing. This will also ensure that every `RedrawRequested`
// event will be delivered in time.
self.with_state(|state| {
shim::handle_window_requests(state);
});
// Process 'new' pending updates from compositor.
self.with_state(|state| {
window_user_requests.clear();
window_user_requests.extend(
state
.window_user_requests
.iter_mut()
.map(|(wid, window_request)| (*wid, mem::take(window_request))),
);
});
// Handle RedrawRequested events.
for (window_id, window_update) in window_updates.iter() {
for (window_id, mut window_request) in window_user_requests.iter() {
// Handle refresh of the frame.
if window_update.refresh_frame {
if window_request.refresh_frame {
self.with_state(|state| {
let window_handle = state.window_map.get_mut(window_id).unwrap();
window_handle.window.refresh();
if !window_update.redraw_requested {
window_handle.window.surface().commit();
}
});
// In general refreshing the frame requires surface commit, those force user
// to redraw.
window_request.redraw_requested = true;
}
// Handle redraw request.
if window_update.redraw_requested {
if window_request.redraw_requested {
sticky_exit_callback(
Event::RedrawRequested(crate::window::WindowId(*window_id)),
&self.window_target,

View File

@@ -3,7 +3,9 @@
use std::collections::HashMap;
use super::EventSink;
use crate::platform_impl::wayland::window::shim::{WindowHandle, WindowUpdate};
use crate::platform_impl::wayland::window::shim::{
WindowCompositorUpdate, WindowHandle, WindowUserRequest,
};
use crate::platform_impl::wayland::WindowId;
/// Wrapper to carry winit's state.
@@ -12,10 +14,14 @@ pub struct WinitState {
/// event loop and forwarded downstream afterwards.
pub event_sink: EventSink,
/// Window updates comming from the user requests. Those are separatelly dispatched right after
/// `MainEventsCleared`.
pub window_user_requests: HashMap<WindowId, WindowUserRequest>,
/// Window updates, which are coming from SCTK or the compositor, which require
/// calling back to the winit's downstream. They are handled right in the event loop,
/// unlike the ones coming from buffers on the `WindowHandle`'s.
pub window_updates: HashMap<WindowId, WindowUpdate>,
pub window_compositor_updates: HashMap<WindowId, WindowCompositorUpdate>,
/// Window map containing all SCTK windows. Since those windows aren't allowed
/// to be sent to other threads, they live on the event loop's thread

View File

@@ -88,18 +88,27 @@ pub(super) fn handle_text_input(
_ => return,
};
// Clear preedit at the start of `Done`.
event_sink.push_window_event(
WindowEvent::Ime(Ime::Preedit(String::new(), None)),
window_id,
);
// Send `Commit`.
if let Some(text) = inner.pending_commit.take() {
event_sink.push_window_event(WindowEvent::Ime(Ime::Commit(text)), window_id);
}
// Push preedit string we've got after latest commit.
// Send preedit.
if let Some(preedit) = inner.pending_preedit.take() {
let cursor_range = preedit
.cursor_begin
.map(|b| (b, preedit.cursor_end.unwrap_or(b)));
let event = Ime::Preedit(preedit.text, cursor_range);
event_sink.push_window_event(WindowEvent::Ime(event), window_id);
event_sink.push_window_event(
WindowEvent::Ime(Ime::Preedit(preedit.text, cursor_range)),
window_id,
);
}
}
_ => (),

View File

@@ -30,7 +30,7 @@ use super::{EventLoopWindowTarget, WindowId};
pub mod shim;
use shim::{WindowHandle, WindowRequest, WindowUpdate};
use shim::{WindowCompositorUpdate, WindowHandle, WindowRequest, WindowUserRequest};
#[cfg(feature = "sctk-adwaita")]
pub type WinitFrame = sctk_adwaita::AdwaitaFrame;
@@ -94,11 +94,20 @@ impl Window {
// Get the window that received the event.
let window_id = super::make_wid(&surface);
let mut window_update = winit_state.window_updates.get_mut(&window_id).unwrap();
let mut window_compositor_update = winit_state
.window_compositor_updates
.get_mut(&window_id)
.unwrap();
// Mark that we need a frame refresh on the DPI change.
winit_state
.window_user_requests
.get_mut(&window_id)
.unwrap()
.refresh_frame = true;
// Set pending scale factor.
window_update.scale_factor = Some(scale);
window_update.redraw_requested = true;
window_compositor_update.scale_factor = Some(scale);
surface.set_buffer_scale(scale);
})
@@ -128,11 +137,19 @@ impl Window {
use sctk::window::{Event, State};
let winit_state = dispatch_data.get::<WinitState>().unwrap();
let mut window_update = winit_state.window_updates.get_mut(&window_id).unwrap();
let mut window_compositor_update = winit_state
.window_compositor_updates
.get_mut(&window_id)
.unwrap();
let mut window_user_requests = winit_state
.window_user_requests
.get_mut(&window_id)
.unwrap();
match event {
Event::Refresh => {
window_update.refresh_frame = true;
window_user_requests.refresh_frame = true;
}
Event::Configure { new_size, states } => {
let is_maximized = states.contains(&State::Maximized);
@@ -140,14 +157,13 @@ impl Window {
let is_fullscreen = states.contains(&State::Fullscreen);
fullscreen_clone.store(is_fullscreen, Ordering::Relaxed);
window_update.refresh_frame = true;
window_update.redraw_requested = true;
window_user_requests.refresh_frame = true;
if let Some((w, h)) = new_size {
window_update.size = Some(LogicalSize::new(w, h));
window_compositor_update.size = Some(LogicalSize::new(w, h));
}
}
Event::Close => {
window_update.close_window = true;
window_compositor_update.close_window = true;
}
}
},
@@ -234,9 +250,9 @@ impl Window {
let size = Arc::new(Mutex::new(LogicalSize::new(width, height)));
// We should trigger redraw and commit the surface for the newly created window.
let mut window_update = WindowUpdate::new();
window_update.refresh_frame = true;
window_update.redraw_requested = true;
let mut window_user_request = WindowUserRequest::new();
window_user_request.refresh_frame = true;
window_user_request.redraw_requested = true;
let window_id = super::make_wid(&surface);
let window_requests = Arc::new(Mutex::new(Vec::with_capacity(64)));
@@ -262,9 +278,13 @@ impl Window {
.event_sink
.push_window_event(crate::event::WindowEvent::Focused(false), window_id);
// Add state for the window.
winit_state
.window_updates
.insert(window_id, WindowUpdate::new());
.window_user_requests
.insert(window_id, window_user_request);
winit_state
.window_compositor_updates
.insert(window_id, WindowCompositorUpdate::new());
let windowing_features = event_loop_window_target.windowing_features;

View File

@@ -98,56 +98,38 @@ pub enum WindowRequest {
Close,
}
/// Pending update to a window from SCTK window.
#[derive(Debug, Clone, Copy)]
pub struct WindowUpdate {
// The window update comming from the compositor.
#[derive(Default, Debug, Clone, Copy)]
pub struct WindowCompositorUpdate {
/// New window size.
pub size: Option<LogicalSize<u32>>,
/// New scale factor.
pub scale_factor: Option<i32>,
/// Close the window.
pub close_window: bool,
}
impl WindowCompositorUpdate {
pub fn new() -> Self {
Default::default()
}
}
/// Pending update to a window requested by the user.
#[derive(Default, Debug, Clone, Copy)]
pub struct WindowUserRequest {
/// Whether `redraw` was requested.
pub redraw_requested: bool,
/// Wether the frame should be refreshed.
pub refresh_frame: bool,
/// Close the window.
pub close_window: bool,
}
impl WindowUpdate {
impl WindowUserRequest {
pub fn new() -> Self {
Self {
size: None,
scale_factor: None,
redraw_requested: false,
refresh_frame: false,
close_window: false,
}
}
pub fn take(&mut self) -> Self {
let size = self.size.take();
let scale_factor = self.scale_factor.take();
let redraw_requested = self.redraw_requested;
self.redraw_requested = false;
let refresh_frame = self.refresh_frame;
self.refresh_frame = false;
let close_window = self.close_window;
self.close_window = false;
Self {
size,
scale_factor,
redraw_requested,
refresh_frame,
close_window,
}
Default::default()
}
}
@@ -422,7 +404,8 @@ impl WindowHandle {
#[inline]
pub fn handle_window_requests(winit_state: &mut WinitState) {
let window_map = &mut winit_state.window_map;
let window_updates = &mut winit_state.window_updates;
let window_user_requests = &mut winit_state.window_user_requests;
let window_compositor_updates = &mut winit_state.window_compositor_updates;
let mut windows_to_close: Vec<WindowId> = Vec::new();
// Process the rest of the events.
@@ -478,15 +461,15 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
window_handle.window.set_decorate(decorations);
// We should refresh the frame to apply decorations change.
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.refresh_frame = true;
let window_request = window_user_requests.get_mut(window_id).unwrap();
window_request.refresh_frame = true;
}
#[cfg(feature = "sctk-adwaita")]
WindowRequest::CsdThemeVariant(theme) => {
window_handle.window.set_frame_config(theme.into());
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.refresh_frame = true;
let window_requst = window_user_requests.get_mut(window_id).unwrap();
window_requst.refresh_frame = true;
}
#[cfg(not(feature = "sctk-adwaita"))]
WindowRequest::CsdThemeVariant(_) => {}
@@ -494,29 +477,29 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
window_handle.window.set_resizable(resizeable);
// We should refresh the frame to update button state.
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.refresh_frame = true;
let window_request = window_user_requests.get_mut(window_id).unwrap();
window_request.refresh_frame = true;
}
WindowRequest::Title(title) => {
window_handle.window.set_title(title);
// We should refresh the frame to draw new title.
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.refresh_frame = true;
let window_request = window_user_requests.get_mut(window_id).unwrap();
window_request.refresh_frame = true;
}
WindowRequest::MinSize(size) => {
let size = size.map(|size| (size.width, size.height));
window_handle.window.set_min_size(size);
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.redraw_requested = true;
let window_request = window_user_requests.get_mut(window_id).unwrap();
window_request.refresh_frame = true;
}
WindowRequest::MaxSize(size) => {
let size = size.map(|size| (size.width, size.height));
window_handle.window.set_max_size(size);
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.redraw_requested = true;
let window_request = window_user_requests.get_mut(window_id).unwrap();
window_request.refresh_frame = true;
}
WindowRequest::FrameSize(size) => {
if !window_handle.is_resizable.get() {
@@ -530,21 +513,21 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
window_handle.window.resize(size.width, size.height);
// We should refresh the frame after resize.
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.refresh_frame = true;
let window_request = window_user_requests.get_mut(window_id).unwrap();
window_request.refresh_frame = true;
}
WindowRequest::PassthroughMouseInput(passthrough) => {
window_handle.passthrough_mouse_input(passthrough);
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.refresh_frame = true;
let window_request = window_user_requests.get_mut(window_id).unwrap();
window_request.refresh_frame = true;
}
WindowRequest::Attention(request_type) => {
window_handle.set_user_attention(request_type);
}
WindowRequest::Redraw => {
let window_update = window_updates.get_mut(window_id).unwrap();
window_update.redraw_requested = true;
let window_request = window_user_requests.get_mut(window_id).unwrap();
window_request.redraw_requested = true;
}
WindowRequest::Close => {
// The window was requested to be closed.
@@ -561,7 +544,8 @@ pub fn handle_window_requests(winit_state: &mut WinitState) {
// Close the windows.
for window in windows_to_close {
let _ = window_map.remove(&window);
let _ = window_updates.remove(&window);
let _ = window_user_requests.remove(&window);
let _ = window_compositor_updates.remove(&window);
}
}

View File

@@ -614,6 +614,12 @@ impl<T: 'static> EventProcessor<T> {
// If we're composing right now, send the string we've got from X11 via
// Ime::Commit.
if self.is_composing && keycode == 0 && !written.is_empty() {
let event = Event::WindowEvent {
window_id,
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
};
callback(event);
let event = Event::WindowEvent {
window_id,
event: WindowEvent::Ime(Ime::Commit(written)),

View File

@@ -108,17 +108,17 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
let mut new_contexts = HashMap::new();
for (window, old_context) in (*inner).contexts.iter() {
let spot = old_context.as_ref().map(|old_context| old_context.ic_spot);
let is_allowed = old_context
let style = old_context
.as_ref()
.map(|old_context| old_context.is_allowed)
.map(|old_context| old_context.style)
.unwrap_or_default();
let new_context = {
let result = ImeContext::new(
xconn,
new_im.im,
style,
*window,
spot,
is_allowed,
(*inner).event_sender.clone(),
);
if result.is_err() {
@@ -132,7 +132,7 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
// If we've made it this far, everything succeeded.
let _ = (*inner).destroy_all_contexts_if_necessary();
let _ = (*inner).close_im_if_necessary();
(*inner).im = new_im.im;
(*inner).im = Some(new_im);
(*inner).contexts = new_contexts;
(*inner).is_destroyed = false;
(*inner).is_fallback = is_fallback;

View File

@@ -1,12 +1,14 @@
use std::mem::transmute;
use std::ffi::CStr;
use std::os::raw::c_short;
use std::ptr;
use std::sync::Arc;
use std::{mem, ptr};
use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct};
use crate::platform_impl::platform::x11::ime::input_method::{Style, XIMStyle};
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender};
use super::{ffi, util, XConnection, XError};
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender};
use std::ffi::CStr;
use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct};
/// IME creation error.
#[derive(Debug)]
@@ -163,7 +165,7 @@ struct PreeditCallbacks {
impl PreeditCallbacks {
pub fn new(client_data: ffi::XPointer) -> PreeditCallbacks {
let start_callback = create_xim_callback(client_data, unsafe {
transmute(preedit_start_callback as usize)
mem::transmute(preedit_start_callback as usize)
});
let done_callback = create_xim_callback(client_data, preedit_done_callback);
let caret_callback = create_xim_callback(client_data, preedit_caret_callback);
@@ -190,9 +192,9 @@ struct ImeContextClientData {
// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled
// through `ImeInner`.
pub struct ImeContext {
pub(super) ic: ffi::XIC,
pub(super) ic_spot: ffi::XPoint,
pub(super) is_allowed: bool,
pub(crate) ic: ffi::XIC,
pub(crate) ic_spot: ffi::XPoint,
pub(crate) style: Style,
// Since the data is passed shared between X11 XIM callbacks, but couldn't be direclty free from
// there we keep the pointer to automatically deallocate it.
_client_data: Box<ImeContextClientData>,
@@ -202,9 +204,9 @@ impl ImeContext {
pub unsafe fn new(
xconn: &Arc<XConnection>,
im: ffi::XIM,
style: Style,
window: ffi::Window,
ic_spot: Option<ffi::XPoint>,
is_allowed: bool,
event_sender: ImeEventSender,
) -> Result<Self, ImeContextCreationError> {
let client_data = Box::into_raw(Box::new(ImeContextClientData {
@@ -214,12 +216,18 @@ impl ImeContext {
cursor_pos: 0,
}));
let ic = if is_allowed {
ImeContext::create_ic(xconn, im, window, client_data as ffi::XPointer)
.ok_or(ImeContextCreationError::Null)?
} else {
ImeContext::create_none_ic(xconn, im, window).ok_or(ImeContextCreationError::Null)?
};
let ic = match style as _ {
Style::Preedit(style) => ImeContext::create_preedit_ic(
xconn,
im,
style,
window,
client_data as ffi::XPointer,
),
Style::Nothing(style) => ImeContext::create_nothing_ic(xconn, im, style, window),
Style::None(style) => ImeContext::create_none_ic(xconn, im, style, window),
}
.ok_or(ImeContextCreationError::Null)?;
xconn
.check_errors()
@@ -228,7 +236,7 @@ impl ImeContext {
let mut context = ImeContext {
ic,
ic_spot: ffi::XPoint { x: 0, y: 0 },
is_allowed,
style,
_client_data: Box::from_raw(client_data),
};
@@ -243,12 +251,13 @@ impl ImeContext {
unsafe fn create_none_ic(
xconn: &Arc<XConnection>,
im: ffi::XIM,
style: XIMStyle,
window: ffi::Window,
) -> Option<ffi::XIC> {
let ic = (xconn.xlib.XCreateIC)(
im,
ffi::XNInputStyle_0.as_ptr() as *const _,
ffi::XIMPreeditNone | ffi::XIMStatusNone,
style,
ffi::XNClientWindow_0.as_ptr() as *const _,
window,
ptr::null_mut::<()>(),
@@ -257,9 +266,10 @@ impl ImeContext {
(!ic.is_null()).then(|| ic)
}
unsafe fn create_ic(
unsafe fn create_preedit_ic(
xconn: &Arc<XConnection>,
im: ffi::XIM,
style: XIMStyle,
window: ffi::Window,
client_data: ffi::XPointer,
) -> Option<ffi::XIC> {
@@ -281,32 +291,34 @@ impl ImeContext {
)
.expect("XVaCreateNestedList returned NULL");
let ic = {
let ic = (xconn.xlib.XCreateIC)(
im,
ffi::XNInputStyle_0.as_ptr() as *const _,
ffi::XIMPreeditCallbacks | ffi::XIMStatusNothing,
ffi::XNClientWindow_0.as_ptr() as *const _,
window,
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
preedit_attr.ptr,
ptr::null_mut::<()>(),
);
let ic = (xconn.xlib.XCreateIC)(
im,
ffi::XNInputStyle_0.as_ptr() as *const _,
style,
ffi::XNClientWindow_0.as_ptr() as *const _,
window,
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
preedit_attr.ptr,
ptr::null_mut::<()>(),
);
// If we've failed to create IC with preedit callbacks fallback to normal one.
if ic.is_null() {
(xconn.xlib.XCreateIC)(
im,
ffi::XNInputStyle_0.as_ptr() as *const _,
ffi::XIMPreeditNothing | ffi::XIMStatusNothing,
ffi::XNClientWindow_0.as_ptr() as *const _,
window,
ptr::null_mut::<()>(),
)
} else {
ic
}
};
(!ic.is_null()).then(|| ic)
}
unsafe fn create_nothing_ic(
xconn: &Arc<XConnection>,
im: ffi::XIM,
style: XIMStyle,
window: ffi::Window,
) -> Option<ffi::XIC> {
let ic = (xconn.xlib.XCreateIC)(
im,
ffi::XNInputStyle_0.as_ptr() as *const _,
style,
ffi::XNClientWindow_0.as_ptr() as *const _,
window,
ptr::null_mut::<()>(),
);
(!ic.is_null()).then(|| ic)
}
@@ -325,13 +337,17 @@ impl ImeContext {
xconn.check_errors()
}
pub fn is_allowed(&self) -> bool {
!matches!(self.style, Style::None(_))
}
// Set the spot for preedit text. Setting spot isn't working with libX11 when preedit callbacks
// are being used. Certain IMEs do show selection window, but it's placed in bottom left of the
// window and couldn't be changed.
//
// For me see: https://bugs.freedesktop.org/show_bug.cgi?id=1580.
pub fn set_spot(&mut self, xconn: &Arc<XConnection>, x: c_short, y: c_short) {
if !self.is_allowed || self.ic_spot.x == x && self.ic_spot.y == y {
if !self.is_allowed() || self.ic_spot.x == x && self.ic_spot.y == y {
return;
}

View File

@@ -1,8 +1,11 @@
use std::{collections::HashMap, mem, ptr, sync::Arc};
use std::{collections::HashMap, mem, sync::Arc};
use super::{ffi, XConnection, XError};
use super::{context::ImeContext, input_method::PotentialInputMethods};
use super::{
context::ImeContext,
input_method::{InputMethod, PotentialInputMethods},
};
use crate::platform_impl::platform::x11::ime::ImeEventSender;
pub unsafe fn close_im(xconn: &Arc<XConnection>, im: ffi::XIM) -> Result<(), XError> {
@@ -17,8 +20,7 @@ pub unsafe fn destroy_ic(xconn: &Arc<XConnection>, ic: ffi::XIC) -> Result<(), X
pub struct ImeInner {
pub xconn: Arc<XConnection>,
// WARNING: this is initially null!
pub im: ffi::XIM,
pub im: Option<InputMethod>,
pub potential_input_methods: PotentialInputMethods,
pub contexts: HashMap<ffi::Window, Option<ImeContext>>,
// WARNING: this is initially zeroed!
@@ -38,7 +40,7 @@ impl ImeInner {
) -> Self {
ImeInner {
xconn,
im: ptr::null_mut(),
im: None,
potential_input_methods,
contexts: HashMap::new(),
destroy_callback: unsafe { mem::zeroed() },
@@ -49,8 +51,8 @@ impl ImeInner {
}
pub unsafe fn close_im_if_necessary(&self) -> Result<bool, XError> {
if !self.is_destroyed {
close_im(&self.xconn, self.im).map(|_| true)
if !self.is_destroyed && self.im.is_some() {
close_im(&self.xconn, self.im.as_ref().unwrap().im).map(|_| true)
} else {
Ok(false)
}

View File

@@ -2,7 +2,7 @@ use std::{
env,
ffi::{CStr, CString, IntoStringError},
fmt,
os::raw::c_char,
os::raw::{c_char, c_ulong, c_ushort},
ptr,
sync::Arc,
};
@@ -41,15 +41,97 @@ unsafe fn open_im(xconn: &Arc<XConnection>, locale_modifiers: &CStr) -> Option<f
#[derive(Debug)]
pub struct InputMethod {
pub im: ffi::XIM,
pub preedit_style: Style,
pub none_style: Style,
_name: String,
}
impl InputMethod {
fn new(im: ffi::XIM, name: String) -> Self {
InputMethod { im, _name: name }
fn new(xconn: &Arc<XConnection>, im: ffi::XIM, name: String) -> Option<Self> {
let mut styles: *mut XIMStyles = std::ptr::null_mut();
// Query the styles supported by the XIM.
unsafe {
if !(xconn.xlib.XGetIMValues)(
im,
ffi::XNQueryInputStyle_0.as_ptr() as *const _,
(&mut styles) as *mut _,
std::ptr::null_mut::<()>(),
)
.is_null()
{
return None;
}
}
let mut preedit_style = None;
let mut none_style = None;
unsafe {
std::slice::from_raw_parts((*styles).supported_styles, (*styles).count_styles as _)
.iter()
.for_each(|style| match *style {
XIM_PREEDIT_STYLE => {
preedit_style = Some(Style::Preedit(*style));
}
XIM_NOTHING_STYLE if preedit_style.is_none() => {
preedit_style = Some(Style::Nothing(*style))
}
XIM_NONE_STYLE => none_style = Some(Style::None(*style)),
_ => (),
});
(xconn.xlib.XFree)(styles.cast());
};
if preedit_style.is_none() && none_style.is_none() {
return None;
}
let preedit_style = preedit_style.unwrap_or_else(|| none_style.unwrap());
let none_style = none_style.unwrap_or(preedit_style);
Some(InputMethod {
im,
_name: name,
preedit_style,
none_style,
})
}
}
const XIM_PREEDIT_STYLE: XIMStyle = (ffi::XIMPreeditCallbacks | ffi::XIMStatusNothing) as XIMStyle;
const XIM_NOTHING_STYLE: XIMStyle = (ffi::XIMPreeditNothing | ffi::XIMStatusNothing) as XIMStyle;
const XIM_NONE_STYLE: XIMStyle = (ffi::XIMPreeditNone | ffi::XIMStatusNone) as XIMStyle;
/// Style of the IME context.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Style {
/// Preedit callbacks.
Preedit(XIMStyle),
/// Nothing.
Nothing(XIMStyle),
/// No IME.
None(XIMStyle),
}
impl Default for Style {
fn default() -> Self {
Style::None(XIM_NONE_STYLE)
}
}
#[repr(C)]
#[derive(Debug)]
struct XIMStyles {
count_styles: c_ushort,
supported_styles: *const XIMStyle,
}
pub(crate) type XIMStyle = c_ulong;
#[derive(Debug)]
pub enum InputMethodResult {
/// Input method used locale modifier from `XMODIFIERS` environment variable.
@@ -175,7 +257,7 @@ impl PotentialInputMethod {
pub fn open_im(&mut self, xconn: &Arc<XConnection>) -> Option<InputMethod> {
let im = unsafe { open_im(xconn, &self.name.c_string) };
self.successful = Some(im.is_some());
im.map(|im| InputMethod::new(im, self.name.string.clone()))
im.and_then(|im| InputMethod::new(xconn, im, self.name.string.clone()))
}
}

View File

@@ -17,8 +17,9 @@ use self::{
callbacks::*,
context::ImeContext,
inner::{close_im, ImeInner},
input_method::PotentialInputMethods,
input_method::{PotentialInputMethods, Style},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ImeEvent {
@@ -87,7 +88,6 @@ impl Ime {
let is_fallback = input_method.is_fallback();
if let Some(input_method) = input_method.ok() {
inner.im = input_method.im;
inner.is_fallback = is_fallback;
unsafe {
let result = set_destroy_callback(&xconn, input_method.im, &*inner)
@@ -97,6 +97,7 @@ impl Ime {
}
result?;
}
inner.im = Some(input_method);
Ok(Ime { xconn, inner })
} else {
Err(ImeCreationError::OpenFailure(inner.potential_input_methods))
@@ -120,11 +121,35 @@ impl Ime {
// Create empty entry in map, so that when IME is rebuilt, this window has a context.
None
} else {
let event = if with_preedit {
ImeEvent::Enabled
let im = self.inner.im.as_ref().unwrap();
let style = if with_preedit {
im.preedit_style
} else {
// There's no IME without preedit.
im.none_style
};
let context = unsafe {
ImeContext::new(
&self.inner.xconn,
im.im,
style,
window,
None,
self.inner.event_sender.clone(),
)?
};
// Check the state on the context, since it could fail to enable or disable preedit.
let event = if matches!(style, Style::None(_)) {
if with_preedit {
debug!("failed to create IME context with preedit support.")
}
ImeEvent::Disabled
} else {
if !with_preedit {
debug!("failed to create IME context without preedit support.")
}
ImeEvent::Enabled
};
self.inner
@@ -132,17 +157,9 @@ impl Ime {
.send((window, event))
.expect("Failed to send enabled event");
Some(unsafe {
ImeContext::new(
&self.inner.xconn,
self.inner.im,
window,
None,
with_preedit,
self.inner.event_sender.clone(),
)
}?)
Some(context)
};
self.inner.contexts.insert(window, context);
Ok(!self.is_destroyed())
}
@@ -206,7 +223,7 @@ impl Ime {
}
if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
if allowed == context.is_allowed {
if allowed == context.is_allowed() {
return;
}
}

View File

@@ -371,15 +371,15 @@ impl UnownedWindow {
} else {
max_inner_size = Some(dimensions.into());
min_inner_size = Some(dimensions.into());
let mut shared_state = window.shared_state.get_mut();
shared_state.min_inner_size = window_attrs.min_inner_size;
shared_state.max_inner_size = window_attrs.max_inner_size;
shared_state.resize_increments = pl_attribs.resize_increments;
shared_state.base_size = pl_attribs.base_size;
}
}
let mut shared_state = window.shared_state.get_mut();
shared_state.min_inner_size = min_inner_size.map(Into::into);
shared_state.max_inner_size = max_inner_size.map(Into::into);
shared_state.resize_increments = pl_attribs.resize_increments;
shared_state.base_size = pl_attribs.base_size;
let mut normal_hints = util::NormalHints::new(xconn);
normal_hints.set_position(position.map(|PhysicalPosition { x, y }| (x, y)));
normal_hints.set_size(Some(dimensions));

View File

@@ -641,6 +641,10 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
//let event: id = msg_send![NSApp(), currentEvent];
if state.is_ime_enabled() && !is_control {
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
}));
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::Ime(Ime::Commit(string)),
@@ -1148,12 +1152,27 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
MouseScrollDelta::LineDelta(x as f32, y as f32)
}
};
let phase = match event.phase() {
// The "momentum phase," if any, has higher priority than touch phase (the two should
// be mutually exclusive anyhow, which is why the API is rather incoherent). If no momentum
// phase is recorded (or rather, the started/ended cases of the momentum phase) then we
// report the touch phase.
let phase = match event.momentumPhase() {
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
TouchPhase::Started
}
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
_ => TouchPhase::Moved,
NSEventPhase::NSEventPhaseEnded | NSEventPhase::NSEventPhaseCancelled => {
TouchPhase::Ended
}
_ => match event.phase() {
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
TouchPhase::Started
}
NSEventPhase::NSEventPhaseEnded | NSEventPhase::NSEventPhaseCancelled => {
TouchPhase::Ended
}
_ => TouchPhase::Moved,
},
};
let device_event = Event::DeviceEvent {

View File

@@ -25,9 +25,9 @@ use windows_sys::Win32::{
Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE,
Foundation::{BOOL, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_TIMEOUT, WPARAM},
Graphics::Gdi::{
ClientToScreen, GetMonitorInfoW, GetUpdateRect, MonitorFromRect, MonitorFromWindow,
RedrawWindow, ScreenToClient, ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL,
RDW_INTERNALPAINT, SC_SCREENSAVE,
GetMonitorInfoW, GetUpdateRect, MonitorFromRect, MonitorFromWindow, RedrawWindow,
ScreenToClient, ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT,
SC_SCREENSAVE,
},
Media::{timeBeginPeriod, timeEndPeriod, timeGetDevCaps, TIMECAPS, TIMERR_NOERROR},
System::{Ole::RevokeDragDrop, Threading::GetCurrentThreadId, WindowsProgramming::INFINITE},
@@ -37,7 +37,7 @@ use windows_sys::Win32::{
Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW},
KeyboardAndMouse::{
MapVirtualKeyA, ReleaseCapture, SetCapture, TrackMouseEvent, TME_LEAVE,
TRACKMOUSEEVENT, VK_F4,
TRACKMOUSEEVENT,
},
Pointer::{
POINTER_FLAG_DOWN, POINTER_FLAG_UP, POINTER_FLAG_UPDATE, POINTER_INFO,
@@ -50,23 +50,23 @@ use windows_sys::Win32::{
RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
},
WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect,
GetCursorPos, GetMessageW, GetWindowLongW, LoadCursorW, MsgWaitForMultipleObjectsEx,
PeekMessageW, PostMessageW, PostThreadMessageW, RegisterClassExW,
RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage, CREATESTRUCTW,
GIDC_ARRIVAL, GIDC_REMOVAL, GWL_EXSTYLE, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT,
MAPVK_VK_TO_VSC, MINMAXINFO, MSG, MWMO_INPUTAVAILABLE, PM_NOREMOVE, PM_QS_PAINT,
PM_REMOVE, PT_PEN, PT_TOUCH, QS_ALLEVENTS, RI_KEY_E0, RI_KEY_E1, RI_MOUSE_WHEEL,
SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE,
SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WM_CAPTURECHANGED, WM_CHAR, WM_CLOSE, WM_CREATE,
WM_DESTROY, WM_DPICHANGED, WM_DROPFILES, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE,
WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT,
WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP,
WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCREATE, WM_NCDESTROY,
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetCursorPos,
GetMessageW, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
PostThreadMessageW, RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos,
TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA,
HTCAPTION, HTCLIENT, MAPVK_VK_TO_VSC, MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE,
NCCALCSIZE_PARAMS, PM_NOREMOVE, PM_QS_PAINT, PM_REMOVE, PT_PEN, PT_TOUCH, QS_ALLEVENTS,
RI_KEY_E0, RI_KEY_E1, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
WM_CAPTURECHANGED, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
WM_DROPFILES, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION,
WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT,
WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN,
WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE,
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE,
WM_SYSCHAR, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED,
WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED,
WM_WINDOWPOSCHANGING, WM_XBUTTONDOWN, WM_XBUTTONUP, WNDCLASSEXW, WS_EX_LAYERED,
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP,
WS_VISIBLE,
@@ -77,7 +77,9 @@ use windows_sys::Win32::{
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
event::{DeviceEvent, Event, Force, Ime, KeyboardInput, Touch, TouchPhase, WindowEvent},
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
event_loop::{
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
},
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::platform::{
dark_mode::try_theme,
@@ -207,7 +209,10 @@ impl<T: 'static> EventLoop<T> {
let thread_msg_sender =
insert_event_target_window_data::<T>(thread_msg_target, runner_shared.clone());
raw_input::register_all_mice_and_keyboards_for_raw_input(thread_msg_target);
raw_input::register_all_mice_and_keyboards_for_raw_input(
thread_msg_target,
Default::default(),
);
EventLoop {
thread_msg_sender,
@@ -322,6 +327,10 @@ impl<T> EventLoopWindowTarget<T> {
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Windows(WindowsDisplayHandle::empty())
}
pub fn set_device_event_filter(&self, filter: DeviceEventFilter) {
raw_input::register_all_mice_and_keyboards_for_raw_input(self.thread_msg_target, filter);
}
}
/// Returns the id of the main thread.
@@ -624,10 +633,12 @@ pub static TASKBAR_CREATED: Lazy<u32> =
Lazy::new(|| unsafe { RegisterWindowMessageA("TaskbarCreated\0".as_ptr()) });
fn create_event_target_window<T: 'static>() -> HWND {
use windows_sys::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
use windows_sys::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
unsafe {
let class = WNDCLASSEXW {
cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
style: 0,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(thread_event_target_callback::<T>),
cbClsExtra: 0,
cbWndExtra: 0,
@@ -959,6 +970,32 @@ unsafe fn public_window_callback_inner<T: 'static>(
// the closure to catch_unwind directly so that the match body indendation wouldn't change and
// the git blame and history would be preserved.
let callback = || match msg {
WM_NCCALCSIZE => {
let window_flags = userdata.window_state.lock().window_flags;
if wparam == 0 || window_flags.contains(WindowFlags::MARKER_DECORATIONS) {
return DefWindowProcW(window, msg, wparam, lparam);
}
// Extend the client area to cover the whole non-client area.
// https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-nccalcsize#remarks
//
// HACK(msiglreith): To add the drop shadow we slightly tweak the non-client area.
// This leads to a small black 1px border on the top. Adding a margin manually
// on all 4 borders would result in the caption getting drawn by the DWM.
//
// Another option would be to allow the DWM to paint inside the client area.
// Unfortunately this results in janky resize behavior, where the compositor is
// ahead of the window surface. Currently, there seems no option to achieve this
// with the Windows API.
if window_flags.contains(WindowFlags::MARKER_UNDECORATED_SHADOW) {
let params = &mut *(lparam as *mut NCCALCSIZE_PARAMS);
params.rgrc[0].top += 1;
params.rgrc[0].bottom += 1;
}
0
}
WM_ENTERSIZEMOVE => {
userdata
.window_state
@@ -1040,7 +1077,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
const NOMOVE_OR_NOSIZE: u32 = SWP_NOMOVE | SWP_NOSIZE;
let new_rect = if window_pos.flags & NOMOVE_OR_NOSIZE != 0 {
let cur_rect = util::get_window_rect(window)
let cur_rect = util::WindowArea::Outer.get_rect(window)
.expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit");
match window_pos.flags & NOMOVE_OR_NOSIZE {
@@ -1151,7 +1188,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
0
}
WM_CHAR | WM_SYSCHAR => {
WM_CHAR => {
use crate::event::WindowEvent::ReceivedCharacter;
use std::char;
let is_high_surrogate = (0xD800..=0xDBFF).contains(&wparam);
@@ -1181,9 +1218,12 @@ unsafe fn public_window_callback_inner<T: 'static>(
});
}
}
0
}
WM_MENUCHAR => (MNC_CLOSE << 16) as isize,
WM_IME_STARTCOMPOSITION => {
let ime_allowed = userdata.window_state.lock().ime_allowed;
if ime_allowed {
@@ -1221,6 +1261,10 @@ unsafe fn public_window_callback_inner<T: 'static>(
if let Some(text) = ime_context.get_composed_text() {
userdata.window_state.lock().ime_state = ImeState::Enabled;
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
});
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Commit(text)),
@@ -1257,6 +1301,10 @@ unsafe fn public_window_callback_inner<T: 'static>(
// trying receiving composing result and commit if exists.
let ime_context = ImeContext::current(window);
if let Some(text) = ime_context.get_composed_text() {
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
});
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::Ime(Ime::Commit(text)),
@@ -1426,35 +1474,36 @@ unsafe fn public_window_callback_inner<T: 'static>(
WM_KEYDOWN | WM_SYSKEYDOWN => {
use crate::event::{ElementState::Pressed, VirtualKeyCode};
if msg == WM_SYSKEYDOWN && wparam == VK_F4 as usize {
DefWindowProcW(window, msg, wparam, lparam)
} else {
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
update_modifiers(window, userdata);
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
update_modifiers(window, userdata);
#[allow(deprecated)]
#[allow(deprecated)]
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Pressed,
scancode,
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
// consistent with the other platforms we'll emit a delete character here.
if vkey == Some(VirtualKeyCode::Delete) {
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
state: Pressed,
scancode,
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
},
is_synthetic: false,
},
event: WindowEvent::ReceivedCharacter('\u{7F}'),
});
// Windows doesn't emit a delete character by default, but in order to make it
// consistent with the other platforms we'll emit a delete character here.
if vkey == Some(VirtualKeyCode::Delete) {
userdata.send_event(Event::WindowEvent {
window_id: RootWindowId(WindowId(window)),
event: WindowEvent::ReceivedCharacter('\u{7F}'),
});
}
}
}
if msg == WM_SYSKEYDOWN {
DefWindowProcW(window, msg, wparam, lparam)
} else {
0
}
}
@@ -1912,11 +1961,13 @@ unsafe fn public_window_callback_inner<T: 'static>(
let mmi = lparam as *mut MINMAXINFO;
let window_state = userdata.window_state.lock();
let window_flags = window_state.window_flags;
if window_state.min_size.is_some() || window_state.max_size.is_some() {
if let Some(min_size) = window_state.min_size {
let min_size = min_size.to_physical(window_state.scale_factor);
let (width, height): (u32, u32) = util::adjust_size(window, min_size).into();
let (width, height): (u32, u32) =
window_flags.adjust_size(window, min_size).into();
(*mmi).ptMinTrackSize = POINT {
x: width as i32,
y: height as i32,
@@ -1924,7 +1975,8 @@ unsafe fn public_window_callback_inner<T: 'static>(
}
if let Some(max_size) = window_state.max_size {
let max_size = max_size.to_physical(window_state.scale_factor);
let (width, height): (u32, u32) = util::adjust_size(window, max_size).into();
let (width, height): (u32, u32) =
window_flags.adjust_size(window, max_size).into();
(*mmi).ptMaxTrackSize = POINT {
x: width as i32,
y: height as i32,
@@ -1948,7 +2000,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
let new_scale_factor = dpi_to_scale_factor(new_dpi_x);
let old_scale_factor: f64;
let allow_resize = {
let (allow_resize, window_flags) = {
let mut window_state = userdata.window_state.lock();
old_scale_factor = window_state.scale_factor;
window_state.scale_factor = new_scale_factor;
@@ -1957,12 +2009,11 @@ unsafe fn public_window_callback_inner<T: 'static>(
return 0;
}
window_state.fullscreen.is_none()
&& !window_state.window_flags().contains(WindowFlags::MAXIMIZED)
};
let allow_resize = window_state.fullscreen.is_none()
&& !window_state.window_flags().contains(WindowFlags::MAXIMIZED);
let style = GetWindowLongW(window, GWL_STYLE) as u32;
let style_ex = GetWindowLongW(window, GWL_EXSTYLE) as u32;
(allow_resize, window_state.window_flags)
};
// New size as suggested by Windows.
let suggested_rect = *(lparam as *const RECT);
@@ -1976,28 +2027,18 @@ unsafe fn public_window_callback_inner<T: 'static>(
// let margin_right: i32;
// let margin_bottom: i32;
{
let adjusted_rect =
util::adjust_window_rect_with_styles(window, style, style_ex, suggested_rect)
.unwrap_or(suggested_rect);
let adjusted_rect = window_flags
.adjust_rect(window, suggested_rect)
.unwrap_or(suggested_rect);
margin_left = suggested_rect.left - adjusted_rect.left;
margin_top = suggested_rect.top - adjusted_rect.top;
// margin_right = adjusted_rect.right - suggested_rect.right;
// margin_bottom = adjusted_rect.bottom - suggested_rect.bottom;
}
let old_physical_inner_rect = {
let mut old_physical_inner_rect = mem::zeroed();
GetClientRect(window, &mut old_physical_inner_rect);
let mut origin = mem::zeroed();
ClientToScreen(window, &mut origin);
old_physical_inner_rect.left += origin.x;
old_physical_inner_rect.right += origin.x;
old_physical_inner_rect.top += origin.y;
old_physical_inner_rect.bottom += origin.y;
old_physical_inner_rect
};
let old_physical_inner_rect = util::WindowArea::Inner
.get_rect(window)
.expect("failed to query (old) inner window area");
let old_physical_inner_size = PhysicalSize::new(
(old_physical_inner_rect.right - old_physical_inner_rect.left) as u32,
(old_physical_inner_rect.bottom - old_physical_inner_rect.top) as u32,
@@ -2051,13 +2092,9 @@ unsafe fn public_window_callback_inner<T: 'static>(
bottom: suggested_ul.1 + new_physical_inner_size.height as i32,
};
conservative_rect = util::adjust_window_rect_with_styles(
window,
style,
style_ex,
conservative_rect,
)
.unwrap_or(conservative_rect);
conservative_rect = window_flags
.adjust_rect(window, conservative_rect)
.unwrap_or(conservative_rect);
// If we're dragging the window, offset the window so that the cursor's
// relative horizontal position in the title bar is preserved.

View File

@@ -16,7 +16,10 @@ use crate::{
dpi::PhysicalSize,
event::{Event, StartCause, WindowEvent},
event_loop::ControlFlow,
platform_impl::platform::util,
platform_impl::platform::{
event_loop::{WindowData, GWL_USERDATA},
get_window_long,
},
window::WindowId,
};
@@ -434,11 +437,13 @@ impl<T> BufferedEvent<T> {
new_inner_size: &mut new_inner_size,
},
});
util::set_inner_size_physical(
(window_id.0).0,
new_inner_size.width as _,
new_inner_size.height as _,
);
let window_flags = unsafe {
let userdata =
get_window_long(window_id.0.into(), GWL_USERDATA) as *mut WindowData<T>;
(*userdata).window_state.lock().window_flags
};
window_flags.set_size((window_id.0).0, new_inner_size);
}
}
}

View File

@@ -36,6 +36,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub drag_and_drop: bool,
pub preferred_theme: Option<Theme>,
pub skip_taskbar: bool,
pub decoration_shadow: bool,
}
impl Default for PlatformSpecificWindowBuilderAttributes {
@@ -48,6 +49,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
drag_and_drop: true,
preferred_theme: None,
skip_taskbar: false,
decoration_shadow: false,
}
}
}
@@ -106,6 +108,12 @@ impl From<WindowId> for u64 {
}
}
impl From<WindowId> for HWND {
fn from(window_id: WindowId) -> Self {
window_id.0
}
}
impl From<u64> for WindowId {
fn from(raw_id: u64) -> Self {
Self(raw_id as HWND)

View File

@@ -12,9 +12,9 @@ use windows_sys::Win32::{
Input::{
GetRawInputData, GetRawInputDeviceInfoW, GetRawInputDeviceList,
RegisterRawInputDevices, HRAWINPUT, RAWINPUT, RAWINPUTDEVICE, RAWINPUTDEVICELIST,
RAWINPUTHEADER, RIDEV_DEVNOTIFY, RIDEV_INPUTSINK, RIDI_DEVICEINFO, RIDI_DEVICENAME,
RID_DEVICE_INFO, RID_DEVICE_INFO_HID, RID_DEVICE_INFO_KEYBOARD, RID_DEVICE_INFO_MOUSE,
RID_INPUT, RIM_TYPEHID, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
RAWINPUTHEADER, RIDEV_DEVNOTIFY, RIDEV_INPUTSINK, RIDEV_REMOVE, RIDI_DEVICEINFO,
RIDI_DEVICENAME, RID_DEVICE_INFO, RID_DEVICE_INFO_HID, RID_DEVICE_INFO_KEYBOARD,
RID_DEVICE_INFO_MOUSE, RID_INPUT, RIM_TYPEHID, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
},
WindowsAndMessaging::{
RI_MOUSE_LEFT_BUTTON_DOWN, RI_MOUSE_LEFT_BUTTON_UP, RI_MOUSE_MIDDLE_BUTTON_DOWN,
@@ -23,7 +23,7 @@ use windows_sys::Win32::{
},
};
use crate::{event::ElementState, platform_impl::platform::util};
use crate::{event::ElementState, event_loop::DeviceEventFilter, platform_impl::platform::util};
#[allow(dead_code)]
pub fn get_raw_input_device_list() -> Option<Vec<RAWINPUTDEVICELIST>> {
@@ -138,10 +138,21 @@ pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool {
}
}
pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> bool {
pub fn register_all_mice_and_keyboards_for_raw_input(
mut window_handle: HWND,
filter: DeviceEventFilter,
) -> bool {
// RIDEV_DEVNOTIFY: receive hotplug events
// RIDEV_INPUTSINK: receive events even if we're not in the foreground
let flags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
// RIDEV_REMOVE: don't receive device events (requires NULL hwndTarget)
let flags = match filter {
DeviceEventFilter::Always => {
window_handle = 0;
RIDEV_REMOVE
}
DeviceEventFilter::Unfocused => RIDEV_DEVNOTIFY,
DeviceEventFilter::Never => RIDEV_DEVNOTIFY | RIDEV_INPUTSINK,
};
let devices: [RAWINPUTDEVICE; 2] = [
RAWINPUTDEVICE {

View File

@@ -14,7 +14,7 @@ use windows_sys::{
core::{HRESULT, PCWSTR},
Win32::{
Foundation::{BOOL, HINSTANCE, HWND, RECT},
Graphics::Gdi::{ClientToScreen, InvalidateRgn, HMONITOR},
Graphics::Gdi::{ClientToScreen, HMONITOR},
System::{
LibraryLoader::{GetProcAddress, LoadLibraryA},
SystemServices::IMAGE_DOS_HEADER,
@@ -23,19 +23,16 @@ use windows_sys::{
HiDpi::{DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS},
Input::KeyboardAndMouse::GetActiveWindow,
WindowsAndMessaging::{
AdjustWindowRectEx, ClipCursor, GetClientRect, GetClipCursor, GetMenu,
GetSystemMetrics, GetWindowLongW, GetWindowRect, SetWindowPos, ShowCursor,
GWL_EXSTYLE, GWL_STYLE, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP,
IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE,
IDC_WAIT, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN,
SM_YVIRTUALSCREEN, SWP_ASYNCWINDOWPOS, SWP_NOACTIVATE, SWP_NOMOVE,
SWP_NOREPOSITION, SWP_NOZORDER, WINDOW_EX_STYLE, WINDOW_STYLE,
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowRect,
ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM,
IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT,
SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN,
},
},
},
};
use crate::{dpi::PhysicalSize, window::CursorIcon};
use crate::window::CursorIcon;
pub fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
string.as_ref().encode_wide().chain(once(0)).collect()
@@ -56,114 +53,43 @@ where
bitset & flag == flag
}
pub unsafe fn status_map<T, F: FnMut(&mut T) -> BOOL>(mut fun: F) -> Option<T> {
let mut data: T = mem::zeroed();
if fun(&mut data) != false.into() {
Some(data)
} else {
None
}
}
fn win_to_err<F: FnOnce() -> BOOL>(f: F) -> Result<(), io::Error> {
if f() != false.into() {
pub(crate) fn win_to_err(result: BOOL) -> Result<(), io::Error> {
if result != false.into() {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
unsafe { status_map(|rect| GetWindowRect(hwnd, rect)) }
pub enum WindowArea {
Outer,
Inner,
}
pub fn get_client_rect(hwnd: HWND) -> Result<RECT, io::Error> {
unsafe {
let mut rect = mem::zeroed();
let mut top_left = mem::zeroed();
impl WindowArea {
pub fn get_rect(self, hwnd: HWND) -> Result<RECT, io::Error> {
let mut rect = unsafe { mem::zeroed() };
win_to_err(|| ClientToScreen(hwnd, &mut top_left))?;
win_to_err(|| GetClientRect(hwnd, &mut rect))?;
rect.left += top_left.x;
rect.top += top_left.y;
rect.right += top_left.x;
rect.bottom += top_left.y;
match self {
WindowArea::Outer => {
win_to_err(unsafe { GetWindowRect(hwnd, &mut rect) })?;
}
WindowArea::Inner => unsafe {
let mut top_left = mem::zeroed();
win_to_err(ClientToScreen(hwnd, &mut top_left))?;
win_to_err(GetClientRect(hwnd, &mut rect))?;
rect.left += top_left.x;
rect.top += top_left.y;
rect.right += top_left.x;
rect.bottom += top_left.y;
},
}
Ok(rect)
}
}
pub fn adjust_size(hwnd: HWND, size: PhysicalSize<u32>) -> PhysicalSize<u32> {
let (width, height): (u32, u32) = size.into();
let rect = RECT {
left: 0,
right: width as i32,
top: 0,
bottom: height as i32,
};
let rect = adjust_window_rect(hwnd, rect).unwrap_or(rect);
PhysicalSize::new((rect.right - rect.left) as _, (rect.bottom - rect.top) as _)
}
pub(crate) fn set_inner_size_physical(window: HWND, x: u32, y: u32) {
unsafe {
let rect = adjust_window_rect(
window,
RECT {
top: 0,
left: 0,
bottom: y as i32,
right: x as i32,
},
)
.expect("adjust_window_rect failed");
let outer_x = (rect.right - rect.left).abs() as _;
let outer_y = (rect.top - rect.bottom).abs() as _;
SetWindowPos(
window,
0,
0,
0,
outer_x,
outer_y,
SWP_ASYNCWINDOWPOS | SWP_NOZORDER | SWP_NOREPOSITION | SWP_NOMOVE | SWP_NOACTIVATE,
);
InvalidateRgn(window, 0, false.into());
}
}
pub fn adjust_window_rect(hwnd: HWND, rect: RECT) -> Option<RECT> {
unsafe {
let style = GetWindowLongW(hwnd, GWL_STYLE) as u32;
let style_ex = GetWindowLongW(hwnd, GWL_EXSTYLE) as u32;
adjust_window_rect_with_styles(hwnd, style, style_ex, rect)
}
}
pub fn adjust_window_rect_with_styles(
hwnd: HWND,
style: WINDOW_STYLE,
style_ex: WINDOW_EX_STYLE,
rect: RECT,
) -> Option<RECT> {
unsafe {
status_map(|r| {
*r = rect;
let b_menu = GetMenu(hwnd) != 0;
if let (Some(get_dpi_for_window), Some(adjust_window_rect_ex_for_dpi)) =
(*GET_DPI_FOR_WINDOW, *ADJUST_WINDOW_RECT_EX_FOR_DPI)
{
let dpi = get_dpi_for_window(hwnd);
adjust_window_rect_ex_for_dpi(r, style, b_menu.into(), style_ex, dpi)
} else {
AdjustWindowRectEx(r, style, b_menu.into(), style_ex)
}
})
}
}
pub fn set_cursor_hidden(hidden: bool) {
static HIDDEN: AtomicBool = AtomicBool::new(false);
let changed = HIDDEN.swap(hidden, Ordering::SeqCst) ^ hidden;
@@ -175,7 +101,7 @@ pub fn set_cursor_hidden(hidden: bool) {
pub fn get_cursor_clip() -> Result<RECT, io::Error> {
unsafe {
let mut rect: RECT = mem::zeroed();
win_to_err(|| GetClipCursor(&mut rect)).map(|_| rect)
win_to_err(GetClipCursor(&mut rect)).map(|_| rect)
}
}
@@ -188,7 +114,7 @@ pub fn set_cursor_clip(rect: Option<RECT>) -> Result<(), io::Error> {
.as_ref()
.map(|r| r as *const RECT)
.unwrap_or(ptr::null());
win_to_err(|| ClipCursor(rect_ptr))
win_to_err(ClipCursor(rect_ptr))
}
}

View File

@@ -132,7 +132,7 @@ impl Window {
#[inline]
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
util::get_window_rect(self.hwnd())
util::WindowArea::Outer.get_rect(self.hwnd())
.map(|rect| Ok(PhysicalPosition::new(rect.left as i32, rect.top as i32)))
.expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit")
}
@@ -187,7 +187,8 @@ impl Window {
#[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> {
util::get_window_rect(self.hwnd())
util::WindowArea::Outer
.get_rect(self.hwnd())
.map(|rect| {
PhysicalSize::new(
(rect.right - rect.left) as u32,
@@ -200,7 +201,7 @@ impl Window {
#[inline]
pub fn set_inner_size(&self, size: Size) {
let scale_factor = self.scale_factor();
let (width, height) = size.to_physical::<u32>(scale_factor).into();
let physical_size = size.to_physical::<u32>(scale_factor);
let window_state = Arc::clone(&self.window_state);
let window = self.window.clone();
@@ -211,7 +212,8 @@ impl Window {
});
});
util::set_inner_size_physical(self.hwnd(), width, height);
let window_flags = self.window_state.lock().window_flags;
window_flags.set_size(self.hwnd(), physical_size);
}
#[inline]
@@ -577,7 +579,7 @@ impl Window {
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::DECORATIONS, decorations)
f.set(WindowFlags::MARKER_DECORATIONS, decorations)
});
});
}
@@ -585,7 +587,9 @@ impl Window {
#[inline]
pub fn is_decorated(&self) -> bool {
let window_state = self.window_state.lock();
window_state.window_flags.contains(WindowFlags::DECORATIONS)
window_state
.window_flags
.contains(WindowFlags::MARKER_DECORATIONS)
}
#[inline]
@@ -691,6 +695,19 @@ impl Window {
unsafe { set_skip_taskbar(self.hwnd(), skip) };
}
#[inline]
pub fn set_undecorated_shadow(&self, shadow: bool) {
let window = self.window.clone();
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
let _ = &window;
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
f.set(WindowFlags::MARKER_UNDECORATED_SHADOW, shadow)
});
});
}
#[inline]
pub fn focus_window(&self) {
let window = self.window.clone();
@@ -879,10 +896,17 @@ impl<'a, T: 'static> InitData<'a, T> {
win.set_fullscreen(attributes.fullscreen);
force_window_active(win.window.0);
} else {
let dimensions = attributes
let size = attributes
.inner_size
.unwrap_or_else(|| PhysicalSize::new(800, 600).into());
win.set_inner_size(dimensions);
let max_size = attributes
.max_inner_size
.unwrap_or_else(|| PhysicalSize::new(f64::MAX, f64::MAX).into());
let min_size = attributes
.min_inner_size
.unwrap_or_else(|| PhysicalSize::new(0, 0).into());
let clamped_size = Size::clamp(size, min_size, max_size, win.scale_factor());
win.set_inner_size(clamped_size);
if attributes.maximized {
// Need to set MAXIMIZED after setting `inner_size` as
@@ -891,6 +915,14 @@ impl<'a, T: 'static> InitData<'a, T> {
}
}
// let margins = MARGINS {
// cxLeftWidth: 1,
// cxRightWidth: 1,
// cyTopHeight: 1,
// cyBottomHeight: 1,
// };
// dbg!(DwmExtendFrameIntoClientArea(win.hwnd(), &margins as *const _));
if let Some(position) = attributes.position {
win.set_outer_position(position);
}
@@ -909,7 +941,11 @@ where
let class_name = register_window_class::<T>(&attributes.window_icon, &pl_attribs.taskbar_icon);
let mut window_flags = WindowFlags::empty();
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
window_flags.set(WindowFlags::MARKER_DECORATIONS, attributes.decorations);
window_flags.set(
WindowFlags::MARKER_UNDECORATED_SHADOW,
pl_attribs.decoration_shadow,
);
window_flags.set(WindowFlags::ALWAYS_ON_TOP, attributes.always_on_top);
window_flags.set(
WindowFlags::NO_BACK_BUFFER,
@@ -990,6 +1026,7 @@ unsafe fn register_window_class<T: 'static>(
.map(|icon| icon.inner.as_raw_handle())
.unwrap_or(0);
use windows_sys::Win32::UI::WindowsAndMessaging::COLOR_WINDOWFRAME;
let class = WNDCLASSEXW {
cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
style: CS_HREDRAW | CS_VREDRAW,
@@ -999,7 +1036,7 @@ unsafe fn register_window_class<T: 'static>(
hInstance: util::get_instance_handle(),
hIcon: h_icon,
hCursor: 0, // must be null in order for cursor state to work properly
hbrBackground: 0,
hbrBackground: COLOR_WINDOWFRAME as _,
lpszMenuName: ptr::null(),
lpszClassName: class_name.as_ptr(),
hIconSm: h_icon_small,

View File

@@ -1,5 +1,5 @@
use crate::{
dpi::{PhysicalPosition, Size},
dpi::{PhysicalPosition, PhysicalSize, Size},
event::ModifiersState,
icon::Icon,
platform_impl::platform::{event_loop, util},
@@ -11,14 +11,15 @@ use windows_sys::Win32::{
Foundation::{HWND, RECT},
Graphics::Gdi::InvalidateRgn,
UI::WindowsAndMessaging::{
SendMessageW, SetWindowLongW, SetWindowPos, ShowWindow, GWL_EXSTYLE, GWL_STYLE,
HWND_NOTOPMOST, HWND_TOPMOST, SWP_ASYNCWINDOWPOS, SWP_FRAMECHANGED, SWP_NOACTIVATE,
SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE,
SW_SHOW, WINDOWPLACEMENT, WINDOW_EX_STYLE, WINDOW_STYLE, WS_BORDER, WS_CAPTION, WS_CHILD,
WS_CLIPCHILDREN, WS_CLIPSIBLINGS, WS_EX_ACCEPTFILES, WS_EX_APPWINDOW, WS_EX_LAYERED,
WS_EX_LEFT, WS_EX_NOREDIRECTIONBITMAP, WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_EX_WINDOWEDGE,
WS_MAXIMIZE, WS_MAXIMIZEBOX, WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPED,
WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SIZEBOX, WS_SYSMENU, WS_VISIBLE,
AdjustWindowRectEx, GetMenu, GetWindowLongW, SendMessageW, SetWindowLongW, SetWindowPos,
ShowWindow, GWL_EXSTYLE, GWL_STYLE, HWND_NOTOPMOST, HWND_TOPMOST, SWP_ASYNCWINDOWPOS,
SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOREPOSITION, SWP_NOSIZE, SWP_NOZORDER,
SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOW, WINDOWPLACEMENT, WINDOW_EX_STYLE,
WINDOW_STYLE, WS_BORDER, WS_CAPTION, WS_CHILD, WS_CLIPCHILDREN, WS_CLIPSIBLINGS,
WS_EX_ACCEPTFILES, WS_EX_APPWINDOW, WS_EX_LAYERED, WS_EX_NOREDIRECTIONBITMAP,
WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_EX_WINDOWEDGE, WS_MAXIMIZE, WS_MAXIMIZEBOX,
WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SIZEBOX, WS_SYSMENU,
WS_VISIBLE,
},
};
@@ -76,36 +77,39 @@ bitflags! {
bitflags! {
pub struct WindowFlags: u32 {
const RESIZABLE = 1 << 0;
const DECORATIONS = 1 << 1;
const VISIBLE = 1 << 2;
const ON_TASKBAR = 1 << 3;
const ALWAYS_ON_TOP = 1 << 4;
const NO_BACK_BUFFER = 1 << 5;
const TRANSPARENT = 1 << 6;
const CHILD = 1 << 7;
const MAXIMIZED = 1 << 8;
const POPUP = 1 << 14;
const VISIBLE = 1 << 1;
const ON_TASKBAR = 1 << 2;
const ALWAYS_ON_TOP = 1 << 3;
const NO_BACK_BUFFER = 1 << 4;
const TRANSPARENT = 1 << 5;
const CHILD = 1 << 6;
const MAXIMIZED = 1 << 7;
const POPUP = 1 << 8;
/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
/// included here to make masking easier.
const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 9;
const MARKER_BORDERLESS_FULLSCREEN = 1 << 13;
const MARKER_BORDERLESS_FULLSCREEN = 1 << 10;
/// The `WM_SIZE` event contains some parameters that can effect the state of `WindowFlags`.
/// In most cases, it's okay to let those parameters change the state. However, when we're
/// running the `WindowFlags::apply_diff` function, we *don't* want those parameters to
/// effect our stored state, because the purpose of `apply_diff` is to update the actual
/// window's state to match our stored state. This controls whether to accept those changes.
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 10;
const MARKER_RETAIN_STATE_ON_SIZE = 1 << 11;
const MARKER_IN_SIZE_MOVE = 1 << 11;
const MARKER_IN_SIZE_MOVE = 1 << 12;
const MINIMIZED = 1 << 12;
const MINIMIZED = 1 << 13;
const IGNORE_CURSOR_EVENT = 1 << 15;
const IGNORE_CURSOR_EVENT = 1 << 14;
/// Fully decorated window (incl. caption, border and drop shadow).
const MARKER_DECORATIONS = 1 << 15;
/// Drop shadow for undecorated windows.
const MARKER_UNDECORATED_SHADOW = 1 << 16;
const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits;
const NO_DECORATIONS_AND_MASK = !WindowFlags::RESIZABLE.bits;
}
}
@@ -228,22 +232,22 @@ impl WindowFlags {
if self.contains(WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN) {
self |= WindowFlags::EXCLUSIVE_FULLSCREEN_OR_MASK;
}
if !self.contains(WindowFlags::DECORATIONS) {
self &= WindowFlags::NO_DECORATIONS_AND_MASK;
}
self
}
pub fn to_window_styles(self) -> (WINDOW_STYLE, WINDOW_EX_STYLE) {
let (mut style, mut style_ex) = (WS_OVERLAPPED, WS_EX_LEFT);
// Required styles to properly support common window functionality like aero snap.
let mut style = WS_CAPTION
| WS_MINIMIZEBOX
| WS_BORDER
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
| WS_SYSMENU;
let mut style_ex = WS_EX_WINDOWEDGE | WS_EX_ACCEPTFILES;
if self.contains(WindowFlags::RESIZABLE) {
style |= WS_SIZEBOX | WS_MAXIMIZEBOX;
}
if self.contains(WindowFlags::DECORATIONS) {
style |= WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER;
style_ex = WS_EX_WINDOWEDGE;
}
if self.contains(WindowFlags::VISIBLE) {
style |= WS_VISIBLE;
}
@@ -272,9 +276,6 @@ impl WindowFlags {
style_ex |= WS_EX_TRANSPARENT | WS_EX_LAYERED;
}
style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU;
style_ex |= WS_EX_ACCEPTFILES;
if self.intersects(
WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN | WindowFlags::MARKER_BORDERLESS_FULLSCREEN,
) {
@@ -379,11 +380,69 @@ impl WindowFlags {
}
}
}
pub fn adjust_rect(self, hwnd: HWND, mut rect: RECT) -> Result<RECT, io::Error> {
unsafe {
let mut style = GetWindowLongW(hwnd, GWL_STYLE) as u32;
let style_ex = GetWindowLongW(hwnd, GWL_EXSTYLE) as u32;
// Frameless style implemented by manually overriding the non-client area in `WM_NCCALCSIZE`.
if !self.contains(WindowFlags::MARKER_DECORATIONS) {
style &= !(WS_CAPTION | WS_SIZEBOX);
}
util::win_to_err({
let b_menu = GetMenu(hwnd) != 0;
if let (Some(get_dpi_for_window), Some(adjust_window_rect_ex_for_dpi)) = (
*util::GET_DPI_FOR_WINDOW,
*util::ADJUST_WINDOW_RECT_EX_FOR_DPI,
) {
let dpi = get_dpi_for_window(hwnd);
adjust_window_rect_ex_for_dpi(&mut rect, style, b_menu.into(), style_ex, dpi)
} else {
AdjustWindowRectEx(&mut rect, style, b_menu.into(), style_ex)
}
})?;
Ok(rect)
}
}
pub fn adjust_size(self, hwnd: HWND, size: PhysicalSize<u32>) -> PhysicalSize<u32> {
let (width, height): (u32, u32) = size.into();
let rect = RECT {
left: 0,
right: width as i32,
top: 0,
bottom: height as i32,
};
let rect = self.adjust_rect(hwnd, rect).unwrap_or(rect);
let outer_x = (rect.right - rect.left).abs();
let outer_y = (rect.top - rect.bottom).abs();
PhysicalSize::new(outer_x as _, outer_y as _)
}
pub fn set_size(self, hwnd: HWND, size: PhysicalSize<u32>) {
unsafe {
let (width, height): (u32, u32) = self.adjust_size(hwnd, size).into();
SetWindowPos(
hwnd,
0,
0,
0,
width as _,
height as _,
SWP_ASYNCWINDOWPOS | SWP_NOZORDER | SWP_NOREPOSITION | SWP_NOMOVE | SWP_NOACTIVATE,
);
InvalidateRgn(hwnd, 0, false.into());
}
}
}
impl CursorFlags {
fn refresh_os_cursor(self, window: HWND) -> Result<(), io::Error> {
let client_rect = util::get_client_rect(window)?;
let client_rect = util::WindowArea::Inner.get_rect(window)?;
if util::is_focused(window) {
let cursor_clip = match self.contains(CursorFlags::GRABBED) {

View File

@@ -1066,6 +1066,109 @@ unsafe impl HasRawDisplayHandle for Window {
self.window.raw_display_handle()
}
}
unsafe impl raw_window_handle_04::HasRawWindowHandle for Window {
/// Returns a [`raw_window_handle_04::RawWindowHandle`] for the Window
///
/// This provides backwards compatibility for downstream crates that have not yet
/// upgraded to `raw_window_handle` version 0.5, such as Wgpu version 0.13.
///
/// ## Platform-specific
///
/// ### Android
///
/// Only available after receiving [`Event::Resumed`] and before [`Event::Suspended`]. *If you
/// try to get the handle outside of that period, this function will panic*!
///
/// Make sure to release or destroy any resources created from this `RawWindowHandle` (ie. Vulkan
/// or OpenGL surfaces) before returning from [`Event::Suspended`], at which point Android will
/// release the underlying window/surface: any subsequent interaction is undefined behavior.
///
/// [`Event::Resumed`]: crate::event::Event::Resumed
/// [`Event::Suspended`]: crate::event::Event::Suspended
fn raw_window_handle(&self) -> raw_window_handle_04::RawWindowHandle {
use raw_window_handle_04::{
AndroidNdkHandle, AppKitHandle, HaikuHandle, OrbitalHandle, UiKitHandle, WaylandHandle,
WebHandle, Win32Handle, WinRtHandle, XcbHandle, XlibHandle,
};
// XXX: Ideally this would be encapsulated either through a
// compatibility API from raw_window_handle_05 or else within the
// backends but since this is only to provide short-term backwards
// compatibility, we just handle the full mapping inline here.
//
// The intention is to remove this trait implementation before Winit
// 0.28, once crates have had time to upgrade to raw_window_handle 0.5
match (self.window.raw_window_handle(), self.window.raw_display_handle()) {
(RawWindowHandle::UiKit(window_handle), _) => {
let mut handle = UiKitHandle::empty();
handle.ui_view = window_handle.ui_view;
handle.ui_window = window_handle.ui_window;
handle.ui_view_controller = window_handle.ui_view_controller;
raw_window_handle_04::RawWindowHandle::UiKit(handle)
},
(RawWindowHandle::AppKit(window_handle), _) => {
let mut handle = AppKitHandle::empty();
handle.ns_window = window_handle.ns_window;
handle.ns_view = window_handle.ns_view;
raw_window_handle_04::RawWindowHandle::AppKit(handle)
},
(RawWindowHandle::Orbital(window_handle), _) => {
let mut handle = OrbitalHandle::empty();
handle.window = window_handle.window;
raw_window_handle_04::RawWindowHandle::Orbital(handle)
},
(RawWindowHandle::Xlib(window_handle), RawDisplayHandle::Xlib(display_handle)) => {
let mut handle = XlibHandle::empty();
handle.display = display_handle.display;
handle.window = window_handle.window;
handle.visual_id = window_handle.visual_id;
raw_window_handle_04::RawWindowHandle::Xlib(handle)
},
(RawWindowHandle::Xcb(window_handle), RawDisplayHandle::Xcb(display_handle)) => {
let mut handle = XcbHandle::empty();
handle.connection = display_handle.connection;
handle.window = window_handle.window;
handle.visual_id = window_handle.visual_id;
raw_window_handle_04::RawWindowHandle::Xcb(handle)
},
(RawWindowHandle::Wayland(window_handle), RawDisplayHandle::Wayland(display_handle)) => {
let mut handle = WaylandHandle::empty();
handle.display = display_handle.display;
handle.surface = window_handle.surface;
raw_window_handle_04::RawWindowHandle::Wayland(handle)
},
(RawWindowHandle::Win32(window_handle), _) => {
let mut handle = Win32Handle::empty();
handle.hwnd = window_handle.hwnd;
handle.hinstance = window_handle.hinstance;
raw_window_handle_04::RawWindowHandle::Win32(handle)
},
(RawWindowHandle::WinRt(window_handle), _) => {
let mut handle = WinRtHandle::empty();
handle.core_window = window_handle.core_window;
raw_window_handle_04::RawWindowHandle::WinRt(handle)
},
(RawWindowHandle::Web(window_handle), _) => {
let mut handle = WebHandle::empty();
handle.id = window_handle.id;
raw_window_handle_04::RawWindowHandle::Web(handle)
},
(RawWindowHandle::AndroidNdk(window_handle), _) => {
let mut handle = AndroidNdkHandle::empty();
handle.a_native_window = window_handle.a_native_window;
raw_window_handle_04::RawWindowHandle::AndroidNdk(handle)
},
(RawWindowHandle::Haiku(window_handle), _) => {
let mut handle = HaikuHandle::empty();
handle.b_window = window_handle.b_window;
handle.b_direct_window = window_handle.b_direct_window;
raw_window_handle_04::RawWindowHandle::Haiku(handle)
},
_ => panic!("No HasRawWindowHandle version 0.4 backwards compatibility for new Winit window type"),
}
}
}
/// The behavior of cursor grabbing.
///