mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
17 Commits
dependabot
...
v0.27.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7037c6ad0 | ||
|
|
51986bce26 | ||
|
|
429cc58f98 | ||
|
|
81303d81a8 | ||
|
|
adfa5bd275 | ||
|
|
66319c571c | ||
|
|
f1470d1ab7 | ||
|
|
79dc6bf4ab | ||
|
|
ad0520e935 | ||
|
|
156fa375ea | ||
|
|
2a2733b2d1 | ||
|
|
7af1163ee7 | ||
|
|
229dbffa17 | ||
|
|
a5457b24c2 | ||
|
|
5645bb1459 | ||
|
|
77cd021c02 | ||
|
|
2d732069a9 |
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -67,14 +67,10 @@ jobs:
|
|||||||
targets: ${{ matrix.platform.target }}
|
targets: ${{ matrix.platform.target }}
|
||||||
components: clippy
|
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
|
- name: Install Linux dependencies
|
||||||
if: (matrix.platform.os == 'ubuntu-latest')
|
if: (matrix.platform.os == 'ubuntu-latest')
|
||||||
run: sudo apt-get update && sudo apt-get install pkg-config cmake libfreetype6-dev libfontconfig1-dev
|
run: sudo apt-get update && sudo apt-get install pkg-config cmake libfreetype6-dev libfontconfig1-dev
|
||||||
|
|
||||||
- name: Install GCC Multilib
|
- name: Install GCC Multilib
|
||||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
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
|
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install g++-multilib gcc-multilib libfreetype6-dev:i386 libfontconfig1-dev:i386
|
||||||
|
|||||||
18
.github/workflows/publish.yml
vendored
18
.github/workflows/publish.yml
vendored
@@ -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 }}
|
|
||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -8,6 +8,22 @@ And please only add new entries to the top of this list, right below the `# Unre
|
|||||||
|
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
# 0.27.4
|
||||||
|
|
||||||
|
- On Windows, emit `ReceivedCharacter` events on system keybindings.
|
||||||
|
- On Windows, fixed focus event emission on minimize.
|
||||||
|
- On X11, fixed IME crashing during reload.
|
||||||
|
|
||||||
|
# 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)
|
# 0.27.2 (2022-8-12)
|
||||||
|
|
||||||
- On macOS, fixed touch phase reporting when scrolling.
|
- On macOS, fixed touch phase reporting when scrolling.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "winit"
|
name = "winit"
|
||||||
version = "0.27.2"
|
version = "0.27.4"
|
||||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||||
description = "Cross-platform window creation library."
|
description = "Cross-platform window creation library."
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@@ -47,13 +47,13 @@ once_cell = "1.12"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||||
raw_window_handle = { package = "raw-window-handle", version = "0.5" }
|
raw_window_handle = { package = "raw-window-handle", version = "0.5" }
|
||||||
raw_window_handle_04 = { package = "raw-window-handle", version = "0.4" }
|
raw_window_handle_04 = { package = "raw-window-handle", version = "0.4.3" }
|
||||||
bitflags = "1"
|
bitflags = "1"
|
||||||
mint = { version = "0.5.6", optional = true }
|
mint = { version = "0.5.6", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
image = { version = "0.24.0", default-features = false, features = ["png"] }
|
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]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
# Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995
|
# Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
winit = "0.27.2"
|
winit = "0.27.4"
|
||||||
```
|
```
|
||||||
|
|
||||||
## [Documentation](https://docs.rs/winit)
|
## [Documentation](https://docs.rs/winit)
|
||||||
|
|||||||
23
src/event.rs
23
src/event.rs
@@ -321,6 +321,10 @@ pub enum WindowEvent<'a> {
|
|||||||
Resized(PhysicalSize<u32>),
|
Resized(PhysicalSize<u32>),
|
||||||
|
|
||||||
/// The position of the window has changed. Contains the window's new position.
|
/// The position of the window has changed. Contains the window's new position.
|
||||||
|
///
|
||||||
|
/// ## Platform-specific
|
||||||
|
///
|
||||||
|
/// - **iOS / Android / Web / Wayland:** Unsupported.
|
||||||
Moved(PhysicalPosition<i32>),
|
Moved(PhysicalPosition<i32>),
|
||||||
|
|
||||||
/// The window has been requested to close.
|
/// 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:
|
/// the character you want to apply the accent to. This will generate the following event sequence:
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// // Press "`" key
|
/// // Press "`" key
|
||||||
/// Ime::Preedit("`", Some(0), Some(0))
|
/// Ime::Preedit("`", Some((0, 0)))
|
||||||
/// // Press "E" key
|
/// // Press "E" key
|
||||||
|
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
|
||||||
/// Ime::Commit("é")
|
/// Ime::Commit("é")
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@@ -785,14 +790,15 @@ pub struct KeyboardInput {
|
|||||||
/// sequence could be obtained:
|
/// sequence could be obtained:
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// // Press "A" key
|
/// // Press "A" key
|
||||||
/// Ime::Preedit("a", Some(1), Some(1))
|
/// Ime::Preedit("a", Some((1, 1)))
|
||||||
/// // Press "B" key
|
/// // Press "B" key
|
||||||
/// Ime::Preedit("a b", Some(3), Some(3))
|
/// Ime::Preedit("a b", Some((3, 3)))
|
||||||
/// // Press left arrow key
|
/// // Press left arrow key
|
||||||
/// Ime::Preedit("a b", Some(1), Some(1))
|
/// Ime::Preedit("a b", Some((1, 1)))
|
||||||
/// // Press space key
|
/// // Press space key
|
||||||
/// Ime::Preedit("啊b", Some(3), Some(3))
|
/// Ime::Preedit("啊b", Some((3, 3)))
|
||||||
/// // Press space key
|
/// // Press space key
|
||||||
|
/// Ime::Preedit("", None) // Synthetic event generated by winit to clear preedit.
|
||||||
/// Ime::Commit("啊不")
|
/// Ime::Commit("啊不")
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[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.
|
/// 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
|
/// 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.
|
/// The cursor position is byte-wise indexed.
|
||||||
Preedit(String, Option<(usize, usize)>),
|
Preedit(String, Option<(usize, usize)>),
|
||||||
|
|
||||||
/// Notifies when text should be inserted into the editor widget.
|
/// 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),
|
Commit(String),
|
||||||
|
|
||||||
/// Notifies when the IME was disabled.
|
/// Notifies when the IME was disabled.
|
||||||
///
|
///
|
||||||
/// After receiving this event you won't get any more [`Preedit`](Self::Preedit) or
|
/// 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
|
/// also stop issuing IME related requests like [`Window::set_ime_position`] and clear pending
|
||||||
/// preedit text.
|
/// preedit text.
|
||||||
Disabled,
|
Disabled,
|
||||||
|
|||||||
@@ -143,6 +143,11 @@ pub trait WindowExtWindows {
|
|||||||
|
|
||||||
/// Whether to show or hide the window icon in the taskbar.
|
/// Whether to show or hide the window icon in the taskbar.
|
||||||
fn set_skip_taskbar(&self, skip: bool);
|
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 {
|
impl WindowExtWindows for Window {
|
||||||
@@ -175,6 +180,11 @@ impl WindowExtWindows for Window {
|
|||||||
fn set_skip_taskbar(&self, skip: bool) {
|
fn set_skip_taskbar(&self, skip: bool) {
|
||||||
self.window.set_skip_taskbar(skip)
|
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.
|
/// 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.
|
/// Whether show or hide the window icon in the taskbar.
|
||||||
fn with_skip_taskbar(self, skip: bool) -> WindowBuilder;
|
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 {
|
impl WindowBuilderExtWindows for WindowBuilder {
|
||||||
@@ -279,6 +295,12 @@ impl WindowBuilderExtWindows for WindowBuilder {
|
|||||||
self.platform_specific.skip_taskbar = skip;
|
self.platform_specific.skip_taskbar = skip;
|
||||||
self
|
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.
|
/// Additional methods on `MonitorHandle` that are specific to Windows.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::{
|
|||||||
sync::mpsc::{self, Receiver, Sender},
|
sync::mpsc::{self, Receiver, Sender},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use objc::runtime::Object;
|
||||||
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
|
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -113,7 +114,7 @@ impl<T: 'static> EventLoop<T> {
|
|||||||
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
|
let application: *mut Object = msg_send![class!(UIApplication), sharedApplication];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
application,
|
application,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ pub trait NSStringRust: Sized {
|
|||||||
|
|
||||||
impl NSStringRust for id {
|
impl NSStringRust for id {
|
||||||
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> 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 {
|
unsafe fn stringByAppendingString_(self, other: id) -> id {
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ impl MonitorHandle {
|
|||||||
pub fn retained_new(uiscreen: id) -> MonitorHandle {
|
pub fn retained_new(uiscreen: id) -> MonitorHandle {
|
||||||
unsafe {
|
unsafe {
|
||||||
assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS");
|
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 {
|
MonitorHandle {
|
||||||
inner: Inner { uiscreen },
|
inner: Inner { uiscreen },
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
|
|||||||
let scale_factor: CGFloat = msg_send![object, contentScaleFactor];
|
let scale_factor: CGFloat = msg_send![object, contentScaleFactor];
|
||||||
PhysicalPosition::from_logical::<(f64, f64), f64>(
|
PhysicalPosition::from_logical::<(f64, f64), f64>(
|
||||||
(logical_location.x as _, logical_location.y as _),
|
(logical_location.x as _, logical_location.y as _),
|
||||||
scale_factor,
|
scale_factor as f64,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
touch_events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
|
|||||||
@@ -411,8 +411,8 @@ impl Window {
|
|||||||
|
|
||||||
let frame = match window_attributes.inner_size {
|
let frame = match window_attributes.inner_size {
|
||||||
Some(dim) => {
|
Some(dim) => {
|
||||||
let scale_factor = msg_send![screen, scale];
|
let scale_factor: CGFloat = msg_send![screen, scale];
|
||||||
let size = dim.to_logical::<f64>(scale_factor);
|
let size = dim.to_logical::<f64>(scale_factor as f64);
|
||||||
CGRect {
|
CGRect {
|
||||||
origin: screen_bounds.origin,
|
origin: screen_bounds.origin,
|
||||||
size: CGSize {
|
size: CGSize {
|
||||||
@@ -427,8 +427,8 @@ impl Window {
|
|||||||
let view = view::create_view(&window_attributes, &platform_attributes, frame);
|
let view = view::create_view(&window_attributes, &platform_attributes, frame);
|
||||||
|
|
||||||
let gl_or_metal_backed = {
|
let gl_or_metal_backed = {
|
||||||
let view_class: id = msg_send![view, class];
|
let view_class: *const Class = msg_send![view, class];
|
||||||
let layer_class: id = msg_send![view_class, layerClass];
|
let layer_class: *const Class = msg_send![view_class, layerClass];
|
||||||
let is_metal: BOOL =
|
let is_metal: BOOL =
|
||||||
msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
|
msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
|
||||||
let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
|
let is_gl: BOOL = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
|
||||||
|
|||||||
@@ -88,18 +88,27 @@ pub(super) fn handle_text_input(
|
|||||||
_ => return,
|
_ => 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() {
|
if let Some(text) = inner.pending_commit.take() {
|
||||||
event_sink.push_window_event(WindowEvent::Ime(Ime::Commit(text)), window_id);
|
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() {
|
if let Some(preedit) = inner.pending_preedit.take() {
|
||||||
let cursor_range = preedit
|
let cursor_range = preedit
|
||||||
.cursor_begin
|
.cursor_begin
|
||||||
.map(|b| (b, preedit.cursor_end.unwrap_or(b)));
|
.map(|b| (b, preedit.cursor_end.unwrap_or(b)));
|
||||||
|
|
||||||
let event = Ime::Preedit(preedit.text, cursor_range);
|
event_sink.push_window_event(
|
||||||
event_sink.push_window_event(WindowEvent::Ime(event), window_id);
|
WindowEvent::Ime(Ime::Preedit(preedit.text, cursor_range)),
|
||||||
|
window_id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|||||||
@@ -614,6 +614,12 @@ impl<T: 'static> EventProcessor<T> {
|
|||||||
// If we're composing right now, send the string we've got from X11 via
|
// If we're composing right now, send the string we've got from X11 via
|
||||||
// Ime::Commit.
|
// Ime::Commit.
|
||||||
if self.is_composing && keycode == 0 && !written.is_empty() {
|
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 {
|
let event = Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::Ime(Ime::Commit(written)),
|
event: WindowEvent::Ime(Ime::Commit(written)),
|
||||||
|
|||||||
@@ -108,17 +108,28 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
|
|||||||
let mut new_contexts = HashMap::new();
|
let mut new_contexts = HashMap::new();
|
||||||
for (window, old_context) in (*inner).contexts.iter() {
|
for (window, old_context) in (*inner).contexts.iter() {
|
||||||
let spot = old_context.as_ref().map(|old_context| old_context.ic_spot);
|
let spot = old_context.as_ref().map(|old_context| old_context.ic_spot);
|
||||||
|
|
||||||
|
// Check if the IME was allowed on that context.
|
||||||
let is_allowed = old_context
|
let is_allowed = old_context
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|old_context| old_context.is_allowed)
|
.map(|old_context| old_context.is_allowed())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
// We can't use the style from the old context here, since it may change on reload, so
|
||||||
|
// pick style from the new XIM based on the old state.
|
||||||
|
let style = if is_allowed {
|
||||||
|
new_im.preedit_style
|
||||||
|
} else {
|
||||||
|
new_im.none_style
|
||||||
|
};
|
||||||
|
|
||||||
let new_context = {
|
let new_context = {
|
||||||
let result = ImeContext::new(
|
let result = ImeContext::new(
|
||||||
xconn,
|
xconn,
|
||||||
new_im.im,
|
new_im.im,
|
||||||
|
style,
|
||||||
*window,
|
*window,
|
||||||
spot,
|
spot,
|
||||||
is_allowed,
|
|
||||||
(*inner).event_sender.clone(),
|
(*inner).event_sender.clone(),
|
||||||
);
|
);
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
@@ -132,7 +143,7 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
|
|||||||
// If we've made it this far, everything succeeded.
|
// If we've made it this far, everything succeeded.
|
||||||
let _ = (*inner).destroy_all_contexts_if_necessary();
|
let _ = (*inner).destroy_all_contexts_if_necessary();
|
||||||
let _ = (*inner).close_im_if_necessary();
|
let _ = (*inner).close_im_if_necessary();
|
||||||
(*inner).im = new_im.im;
|
(*inner).im = Some(new_im);
|
||||||
(*inner).contexts = new_contexts;
|
(*inner).contexts = new_contexts;
|
||||||
(*inner).is_destroyed = false;
|
(*inner).is_destroyed = false;
|
||||||
(*inner).is_fallback = is_fallback;
|
(*inner).is_fallback = is_fallback;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::{mem, ptr};
|
|||||||
|
|
||||||
use x11_dl::xlib::{XIMCallback, XIMPreeditCaretCallbackStruct, XIMPreeditDrawCallbackStruct};
|
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 crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventSender};
|
||||||
|
|
||||||
use super::{ffi, util, XConnection, XError};
|
use super::{ffi, util, XConnection, XError};
|
||||||
@@ -191,9 +192,9 @@ struct ImeContextClientData {
|
|||||||
// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled
|
// still exists on the server. Since `ImeInner` has that awareness, destruction must be handled
|
||||||
// through `ImeInner`.
|
// through `ImeInner`.
|
||||||
pub struct ImeContext {
|
pub struct ImeContext {
|
||||||
pub(super) ic: ffi::XIC,
|
pub(crate) ic: ffi::XIC,
|
||||||
pub(super) ic_spot: ffi::XPoint,
|
pub(crate) ic_spot: ffi::XPoint,
|
||||||
pub(super) is_allowed: bool,
|
pub(crate) style: Style,
|
||||||
// Since the data is passed shared between X11 XIM callbacks, but couldn't be direclty free from
|
// 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.
|
// there we keep the pointer to automatically deallocate it.
|
||||||
_client_data: Box<ImeContextClientData>,
|
_client_data: Box<ImeContextClientData>,
|
||||||
@@ -203,9 +204,9 @@ impl ImeContext {
|
|||||||
pub unsafe fn new(
|
pub unsafe fn new(
|
||||||
xconn: &Arc<XConnection>,
|
xconn: &Arc<XConnection>,
|
||||||
im: ffi::XIM,
|
im: ffi::XIM,
|
||||||
|
style: Style,
|
||||||
window: ffi::Window,
|
window: ffi::Window,
|
||||||
ic_spot: Option<ffi::XPoint>,
|
ic_spot: Option<ffi::XPoint>,
|
||||||
is_allowed: bool,
|
|
||||||
event_sender: ImeEventSender,
|
event_sender: ImeEventSender,
|
||||||
) -> Result<Self, ImeContextCreationError> {
|
) -> Result<Self, ImeContextCreationError> {
|
||||||
let client_data = Box::into_raw(Box::new(ImeContextClientData {
|
let client_data = Box::into_raw(Box::new(ImeContextClientData {
|
||||||
@@ -215,12 +216,18 @@ impl ImeContext {
|
|||||||
cursor_pos: 0,
|
cursor_pos: 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let ic = if is_allowed {
|
let ic = match style as _ {
|
||||||
ImeContext::create_ic(xconn, im, window, client_data as ffi::XPointer)
|
Style::Preedit(style) => ImeContext::create_preedit_ic(
|
||||||
.ok_or(ImeContextCreationError::Null)?
|
xconn,
|
||||||
} else {
|
im,
|
||||||
ImeContext::create_none_ic(xconn, im, window).ok_or(ImeContextCreationError::Null)?
|
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
|
xconn
|
||||||
.check_errors()
|
.check_errors()
|
||||||
@@ -229,7 +236,7 @@ impl ImeContext {
|
|||||||
let mut context = ImeContext {
|
let mut context = ImeContext {
|
||||||
ic,
|
ic,
|
||||||
ic_spot: ffi::XPoint { x: 0, y: 0 },
|
ic_spot: ffi::XPoint { x: 0, y: 0 },
|
||||||
is_allowed,
|
style,
|
||||||
_client_data: Box::from_raw(client_data),
|
_client_data: Box::from_raw(client_data),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -244,12 +251,13 @@ impl ImeContext {
|
|||||||
unsafe fn create_none_ic(
|
unsafe fn create_none_ic(
|
||||||
xconn: &Arc<XConnection>,
|
xconn: &Arc<XConnection>,
|
||||||
im: ffi::XIM,
|
im: ffi::XIM,
|
||||||
|
style: XIMStyle,
|
||||||
window: ffi::Window,
|
window: ffi::Window,
|
||||||
) -> Option<ffi::XIC> {
|
) -> Option<ffi::XIC> {
|
||||||
let ic = (xconn.xlib.XCreateIC)(
|
let ic = (xconn.xlib.XCreateIC)(
|
||||||
im,
|
im,
|
||||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||||
ffi::XIMPreeditNone | ffi::XIMStatusNone,
|
style,
|
||||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||||
window,
|
window,
|
||||||
ptr::null_mut::<()>(),
|
ptr::null_mut::<()>(),
|
||||||
@@ -258,9 +266,10 @@ impl ImeContext {
|
|||||||
(!ic.is_null()).then(|| ic)
|
(!ic.is_null()).then(|| ic)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn create_ic(
|
unsafe fn create_preedit_ic(
|
||||||
xconn: &Arc<XConnection>,
|
xconn: &Arc<XConnection>,
|
||||||
im: ffi::XIM,
|
im: ffi::XIM,
|
||||||
|
style: XIMStyle,
|
||||||
window: ffi::Window,
|
window: ffi::Window,
|
||||||
client_data: ffi::XPointer,
|
client_data: ffi::XPointer,
|
||||||
) -> Option<ffi::XIC> {
|
) -> Option<ffi::XIC> {
|
||||||
@@ -282,32 +291,34 @@ impl ImeContext {
|
|||||||
)
|
)
|
||||||
.expect("XVaCreateNestedList returned NULL");
|
.expect("XVaCreateNestedList returned NULL");
|
||||||
|
|
||||||
let ic = {
|
let ic = (xconn.xlib.XCreateIC)(
|
||||||
let ic = (xconn.xlib.XCreateIC)(
|
im,
|
||||||
im,
|
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
style,
|
||||||
ffi::XIMPreeditCallbacks | ffi::XIMStatusNothing,
|
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
window,
|
||||||
window,
|
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
|
||||||
ffi::XNPreeditAttributes_0.as_ptr() as *const _,
|
preedit_attr.ptr,
|
||||||
preedit_attr.ptr,
|
ptr::null_mut::<()>(),
|
||||||
ptr::null_mut::<()>(),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// If we've failed to create IC with preedit callbacks fallback to normal one.
|
(!ic.is_null()).then(|| ic)
|
||||||
if ic.is_null() {
|
}
|
||||||
(xconn.xlib.XCreateIC)(
|
|
||||||
im,
|
unsafe fn create_nothing_ic(
|
||||||
ffi::XNInputStyle_0.as_ptr() as *const _,
|
xconn: &Arc<XConnection>,
|
||||||
ffi::XIMPreeditNothing | ffi::XIMStatusNothing,
|
im: ffi::XIM,
|
||||||
ffi::XNClientWindow_0.as_ptr() as *const _,
|
style: XIMStyle,
|
||||||
window,
|
window: ffi::Window,
|
||||||
ptr::null_mut::<()>(),
|
) -> Option<ffi::XIC> {
|
||||||
)
|
let ic = (xconn.xlib.XCreateIC)(
|
||||||
} else {
|
im,
|
||||||
ic
|
ffi::XNInputStyle_0.as_ptr() as *const _,
|
||||||
}
|
style,
|
||||||
};
|
ffi::XNClientWindow_0.as_ptr() as *const _,
|
||||||
|
window,
|
||||||
|
ptr::null_mut::<()>(),
|
||||||
|
);
|
||||||
|
|
||||||
(!ic.is_null()).then(|| ic)
|
(!ic.is_null()).then(|| ic)
|
||||||
}
|
}
|
||||||
@@ -326,13 +337,17 @@ impl ImeContext {
|
|||||||
xconn.check_errors()
|
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
|
// 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
|
// are being used. Certain IMEs do show selection window, but it's placed in bottom left of the
|
||||||
// window and couldn't be changed.
|
// window and couldn't be changed.
|
||||||
//
|
//
|
||||||
// For me see: https://bugs.freedesktop.org/show_bug.cgi?id=1580.
|
// 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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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::{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;
|
use crate::platform_impl::platform::x11::ime::ImeEventSender;
|
||||||
|
|
||||||
pub unsafe fn close_im(xconn: &Arc<XConnection>, im: ffi::XIM) -> Result<(), XError> {
|
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 struct ImeInner {
|
||||||
pub xconn: Arc<XConnection>,
|
pub xconn: Arc<XConnection>,
|
||||||
// WARNING: this is initially null!
|
pub im: Option<InputMethod>,
|
||||||
pub im: ffi::XIM,
|
|
||||||
pub potential_input_methods: PotentialInputMethods,
|
pub potential_input_methods: PotentialInputMethods,
|
||||||
pub contexts: HashMap<ffi::Window, Option<ImeContext>>,
|
pub contexts: HashMap<ffi::Window, Option<ImeContext>>,
|
||||||
// WARNING: this is initially zeroed!
|
// WARNING: this is initially zeroed!
|
||||||
@@ -38,7 +40,7 @@ impl ImeInner {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
ImeInner {
|
ImeInner {
|
||||||
xconn,
|
xconn,
|
||||||
im: ptr::null_mut(),
|
im: None,
|
||||||
potential_input_methods,
|
potential_input_methods,
|
||||||
contexts: HashMap::new(),
|
contexts: HashMap::new(),
|
||||||
destroy_callback: unsafe { mem::zeroed() },
|
destroy_callback: unsafe { mem::zeroed() },
|
||||||
@@ -49,8 +51,8 @@ impl ImeInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn close_im_if_necessary(&self) -> Result<bool, XError> {
|
pub unsafe fn close_im_if_necessary(&self) -> Result<bool, XError> {
|
||||||
if !self.is_destroyed {
|
if !self.is_destroyed && self.im.is_some() {
|
||||||
close_im(&self.xconn, self.im).map(|_| true)
|
close_im(&self.xconn, self.im.as_ref().unwrap().im).map(|_| true)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::{
|
|||||||
env,
|
env,
|
||||||
ffi::{CStr, CString, IntoStringError},
|
ffi::{CStr, CString, IntoStringError},
|
||||||
fmt,
|
fmt,
|
||||||
os::raw::c_char,
|
os::raw::{c_char, c_ulong, c_ushort},
|
||||||
ptr,
|
ptr,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
@@ -41,15 +41,97 @@ unsafe fn open_im(xconn: &Arc<XConnection>, locale_modifiers: &CStr) -> Option<f
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InputMethod {
|
pub struct InputMethod {
|
||||||
pub im: ffi::XIM,
|
pub im: ffi::XIM,
|
||||||
|
pub preedit_style: Style,
|
||||||
|
pub none_style: Style,
|
||||||
_name: String,
|
_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputMethod {
|
impl InputMethod {
|
||||||
fn new(im: ffi::XIM, name: String) -> Self {
|
fn new(xconn: &Arc<XConnection>, im: ffi::XIM, name: String) -> Option<Self> {
|
||||||
InputMethod { im, _name: name }
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum InputMethodResult {
|
pub enum InputMethodResult {
|
||||||
/// Input method used locale modifier from `XMODIFIERS` environment variable.
|
/// 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> {
|
pub fn open_im(&mut self, xconn: &Arc<XConnection>) -> Option<InputMethod> {
|
||||||
let im = unsafe { open_im(xconn, &self.name.c_string) };
|
let im = unsafe { open_im(xconn, &self.name.c_string) };
|
||||||
self.successful = Some(im.is_some());
|
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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,9 @@ use self::{
|
|||||||
callbacks::*,
|
callbacks::*,
|
||||||
context::ImeContext,
|
context::ImeContext,
|
||||||
inner::{close_im, ImeInner},
|
inner::{close_im, ImeInner},
|
||||||
input_method::PotentialInputMethods,
|
input_method::{PotentialInputMethods, Style},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum ImeEvent {
|
pub enum ImeEvent {
|
||||||
@@ -87,7 +88,6 @@ impl Ime {
|
|||||||
|
|
||||||
let is_fallback = input_method.is_fallback();
|
let is_fallback = input_method.is_fallback();
|
||||||
if let Some(input_method) = input_method.ok() {
|
if let Some(input_method) = input_method.ok() {
|
||||||
inner.im = input_method.im;
|
|
||||||
inner.is_fallback = is_fallback;
|
inner.is_fallback = is_fallback;
|
||||||
unsafe {
|
unsafe {
|
||||||
let result = set_destroy_callback(&xconn, input_method.im, &*inner)
|
let result = set_destroy_callback(&xconn, input_method.im, &*inner)
|
||||||
@@ -97,6 +97,7 @@ impl Ime {
|
|||||||
}
|
}
|
||||||
result?;
|
result?;
|
||||||
}
|
}
|
||||||
|
inner.im = Some(input_method);
|
||||||
Ok(Ime { xconn, inner })
|
Ok(Ime { xconn, inner })
|
||||||
} else {
|
} else {
|
||||||
Err(ImeCreationError::OpenFailure(inner.potential_input_methods))
|
Err(ImeCreationError::OpenFailure(inner.potential_input_methods))
|
||||||
@@ -120,37 +121,35 @@ impl Ime {
|
|||||||
// Create empty entry in map, so that when IME is rebuilt, this window has a context.
|
// Create empty entry in map, so that when IME is rebuilt, this window has a context.
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
let im = self.inner.im.as_ref().unwrap();
|
||||||
|
let style = if with_preedit {
|
||||||
|
im.preedit_style
|
||||||
|
} else {
|
||||||
|
im.none_style
|
||||||
|
};
|
||||||
|
|
||||||
let context = unsafe {
|
let context = unsafe {
|
||||||
ImeContext::new(
|
ImeContext::new(
|
||||||
&self.inner.xconn,
|
&self.inner.xconn,
|
||||||
self.inner.im,
|
im.im,
|
||||||
|
style,
|
||||||
window,
|
window,
|
||||||
None,
|
None,
|
||||||
with_preedit,
|
|
||||||
self.inner.event_sender.clone(),
|
self.inner.event_sender.clone(),
|
||||||
)
|
)?
|
||||||
.or_else(|_| {
|
};
|
||||||
debug!(
|
|
||||||
"failed to create an IME context {} preedit support",
|
|
||||||
if with_preedit { "with" } else { "without" }
|
|
||||||
);
|
|
||||||
ImeContext::new(
|
|
||||||
&self.inner.xconn,
|
|
||||||
self.inner.im,
|
|
||||||
window,
|
|
||||||
None,
|
|
||||||
!with_preedit,
|
|
||||||
self.inner.event_sender.clone(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}?;
|
|
||||||
|
|
||||||
// Check the state on the context, since it could fail to enable or disable preedit.
|
// Check the state on the context, since it could fail to enable or disable preedit.
|
||||||
let event = if context.is_allowed {
|
let event = if matches!(style, Style::None(_)) {
|
||||||
ImeEvent::Enabled
|
if with_preedit {
|
||||||
} else {
|
debug!("failed to create IME context with preedit support.")
|
||||||
// There's no IME without preedit.
|
}
|
||||||
ImeEvent::Disabled
|
ImeEvent::Disabled
|
||||||
|
} else {
|
||||||
|
if !with_preedit {
|
||||||
|
debug!("failed to create IME context without preedit support.")
|
||||||
|
}
|
||||||
|
ImeEvent::Enabled
|
||||||
};
|
};
|
||||||
|
|
||||||
self.inner
|
self.inner
|
||||||
@@ -160,6 +159,7 @@ impl Ime {
|
|||||||
|
|
||||||
Some(context)
|
Some(context)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.inner.contexts.insert(window, context);
|
self.inner.contexts.insert(window, context);
|
||||||
Ok(!self.is_destroyed())
|
Ok(!self.is_destroyed())
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ impl Ime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(&mut Some(ref mut context)) = self.inner.contexts.get_mut(&window) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -641,6 +641,10 @@ extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_ran
|
|||||||
//let event: id = msg_send![NSApp(), currentEvent];
|
//let event: id = msg_send![NSApp(), currentEvent];
|
||||||
|
|
||||||
if state.is_ime_enabled() && !is_control {
|
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 {
|
AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||||
window_id: WindowId(get_window_id(state.ns_window)),
|
window_id: WindowId(get_window_id(state.ns_window)),
|
||||||
event: WindowEvent::Ime(Ime::Commit(string)),
|
event: WindowEvent::Ime(Ime::Commit(string)),
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ use windows_sys::Win32::{
|
|||||||
Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE,
|
Devices::HumanInterfaceDevice::MOUSE_MOVE_RELATIVE,
|
||||||
Foundation::{BOOL, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_TIMEOUT, WPARAM},
|
Foundation::{BOOL, HANDLE, HWND, LPARAM, LRESULT, POINT, RECT, WAIT_TIMEOUT, WPARAM},
|
||||||
Graphics::Gdi::{
|
Graphics::Gdi::{
|
||||||
ClientToScreen, GetMonitorInfoW, GetUpdateRect, MonitorFromRect, MonitorFromWindow,
|
GetMonitorInfoW, GetUpdateRect, MonitorFromRect, MonitorFromWindow, RedrawWindow,
|
||||||
RedrawWindow, ScreenToClient, ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL,
|
ScreenToClient, ValidateRect, MONITORINFO, MONITOR_DEFAULTTONULL, RDW_INTERNALPAINT,
|
||||||
RDW_INTERNALPAINT, SC_SCREENSAVE,
|
SC_SCREENSAVE,
|
||||||
},
|
},
|
||||||
Media::{timeBeginPeriod, timeEndPeriod, timeGetDevCaps, TIMECAPS, TIMERR_NOERROR},
|
Media::{timeBeginPeriod, timeEndPeriod, timeGetDevCaps, TIMECAPS, TIMERR_NOERROR},
|
||||||
System::{Ole::RevokeDragDrop, Threading::GetCurrentThreadId, WindowsProgramming::INFINITE},
|
System::{Ole::RevokeDragDrop, Threading::GetCurrentThreadId, WindowsProgramming::INFINITE},
|
||||||
@@ -37,7 +37,7 @@ use windows_sys::Win32::{
|
|||||||
Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW},
|
Ime::{GCS_COMPSTR, GCS_RESULTSTR, ISC_SHOWUICOMPOSITIONWINDOW},
|
||||||
KeyboardAndMouse::{
|
KeyboardAndMouse::{
|
||||||
MapVirtualKeyA, ReleaseCapture, SetCapture, TrackMouseEvent, TME_LEAVE,
|
MapVirtualKeyA, ReleaseCapture, SetCapture, TrackMouseEvent, TME_LEAVE,
|
||||||
TRACKMOUSEEVENT, VK_F4,
|
TRACKMOUSEEVENT,
|
||||||
},
|
},
|
||||||
Pointer::{
|
Pointer::{
|
||||||
POINTER_FLAG_DOWN, POINTER_FLAG_UP, POINTER_FLAG_UPDATE, POINTER_INFO,
|
POINTER_FLAG_DOWN, POINTER_FLAG_UP, POINTER_FLAG_UPDATE, POINTER_INFO,
|
||||||
@@ -50,20 +50,20 @@ use windows_sys::Win32::{
|
|||||||
RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
|
RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
|
||||||
},
|
},
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetClientRect,
|
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetCursorPos,
|
||||||
GetCursorPos, GetMessageW, GetWindowLongW, LoadCursorW, MsgWaitForMultipleObjectsEx,
|
GetMessageW, LoadCursorW, MsgWaitForMultipleObjectsEx, PeekMessageW, PostMessageW,
|
||||||
PeekMessageW, PostMessageW, PostThreadMessageW, RegisterClassExW,
|
PostThreadMessageW, RegisterClassExW, RegisterWindowMessageA, SetCursor, SetWindowPos,
|
||||||
RegisterWindowMessageA, SetCursor, SetWindowPos, TranslateMessage, CREATESTRUCTW,
|
TranslateMessage, CREATESTRUCTW, GIDC_ARRIVAL, GIDC_REMOVAL, GWL_STYLE, GWL_USERDATA,
|
||||||
GIDC_ARRIVAL, GIDC_REMOVAL, GWL_EXSTYLE, GWL_STYLE, GWL_USERDATA, HTCAPTION, HTCLIENT,
|
HTCAPTION, HTCLIENT, MAPVK_VK_TO_VSC, MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE,
|
||||||
MAPVK_VK_TO_VSC, MINMAXINFO, MSG, MWMO_INPUTAVAILABLE, PM_NOREMOVE, PM_QS_PAINT,
|
NCCALCSIZE_PARAMS, PM_NOREMOVE, PM_QS_PAINT, PM_REMOVE, PT_PEN, PT_TOUCH, QS_ALLEVENTS,
|
||||||
PM_REMOVE, PT_PEN, PT_TOUCH, QS_ALLEVENTS, RI_KEY_E0, RI_KEY_E1, RI_MOUSE_WHEEL,
|
RI_KEY_E0, RI_KEY_E1, RI_MOUSE_WHEEL, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED,
|
||||||
SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE,
|
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS,
|
||||||
SWP_NOZORDER, WHEEL_DELTA, WINDOWPOS, WM_CAPTURECHANGED, WM_CHAR, WM_CLOSE, WM_CREATE,
|
WM_CAPTURECHANGED, WM_CHAR, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED,
|
||||||
WM_DESTROY, WM_DPICHANGED, WM_DROPFILES, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE,
|
WM_DROPFILES, WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION,
|
||||||
WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT,
|
WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT,
|
||||||
WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP,
|
WM_INPUT_DEVICE_CHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN,
|
||||||
WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP,
|
WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE,
|
||||||
WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCREATE, WM_NCDESTROY,
|
WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, WM_NCDESTROY,
|
||||||
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE,
|
WM_NCLBUTTONDOWN, WM_PAINT, WM_POINTERDOWN, WM_POINTERUP, WM_POINTERUPDATE,
|
||||||
WM_RBUTTONDOWN, WM_RBUTTONUP, WM_SETCURSOR, WM_SETFOCUS, WM_SETTINGCHANGE, WM_SIZE,
|
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_SYSCHAR, WM_SYSCOMMAND, WM_SYSKEYDOWN, WM_SYSKEYUP, WM_TOUCH, WM_WINDOWPOSCHANGED,
|
||||||
@@ -633,10 +633,12 @@ pub static TASKBAR_CREATED: Lazy<u32> =
|
|||||||
Lazy::new(|| unsafe { RegisterWindowMessageA("TaskbarCreated\0".as_ptr()) });
|
Lazy::new(|| unsafe { RegisterWindowMessageA("TaskbarCreated\0".as_ptr()) });
|
||||||
|
|
||||||
fn create_event_target_window<T: 'static>() -> HWND {
|
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 {
|
unsafe {
|
||||||
let class = WNDCLASSEXW {
|
let class = WNDCLASSEXW {
|
||||||
cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
|
cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
|
||||||
style: 0,
|
style: CS_HREDRAW | CS_VREDRAW,
|
||||||
lpfnWndProc: Some(thread_event_target_callback::<T>),
|
lpfnWndProc: Some(thread_event_target_callback::<T>),
|
||||||
cbClsExtra: 0,
|
cbClsExtra: 0,
|
||||||
cbWndExtra: 0,
|
cbWndExtra: 0,
|
||||||
@@ -968,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 closure to catch_unwind directly so that the match body indendation wouldn't change and
|
||||||
// the git blame and history would be preserved.
|
// the git blame and history would be preserved.
|
||||||
let callback = || match msg {
|
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 => {
|
WM_ENTERSIZEMOVE => {
|
||||||
userdata
|
userdata
|
||||||
.window_state
|
.window_state
|
||||||
@@ -1049,7 +1077,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
const NOMOVE_OR_NOSIZE: u32 = SWP_NOMOVE | SWP_NOSIZE;
|
const NOMOVE_OR_NOSIZE: u32 = SWP_NOMOVE | SWP_NOSIZE;
|
||||||
|
|
||||||
let new_rect = if window_pos.flags & NOMOVE_OR_NOSIZE != 0 {
|
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");
|
.expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit");
|
||||||
|
|
||||||
match window_pos.flags & NOMOVE_OR_NOSIZE {
|
match window_pos.flags & NOMOVE_OR_NOSIZE {
|
||||||
@@ -1190,9 +1218,23 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0
|
|
||||||
|
// todo(msiglreith):
|
||||||
|
// Ideally, `WM_SYSCHAR` shouldn't emit a `ReceivedChar` event
|
||||||
|
// indicating user text input. As we lack dedicated support
|
||||||
|
// accelerators/keybindings these events will be additionally
|
||||||
|
// emitted for downstream users.
|
||||||
|
// This means certain key combinations (ie Alt + Space) will
|
||||||
|
// trigger the default system behavior **and** emit a char event.
|
||||||
|
if msg == WM_SYSCHAR {
|
||||||
|
DefWindowProcW(window, msg, wparam, lparam)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WM_MENUCHAR => (MNC_CLOSE << 16) as isize,
|
||||||
|
|
||||||
WM_IME_STARTCOMPOSITION => {
|
WM_IME_STARTCOMPOSITION => {
|
||||||
let ime_allowed = userdata.window_state.lock().ime_allowed;
|
let ime_allowed = userdata.window_state.lock().ime_allowed;
|
||||||
if ime_allowed {
|
if ime_allowed {
|
||||||
@@ -1230,6 +1272,10 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
if let Some(text) = ime_context.get_composed_text() {
|
if let Some(text) = ime_context.get_composed_text() {
|
||||||
userdata.window_state.lock().ime_state = ImeState::Enabled;
|
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 {
|
userdata.send_event(Event::WindowEvent {
|
||||||
window_id: RootWindowId(WindowId(window)),
|
window_id: RootWindowId(WindowId(window)),
|
||||||
event: WindowEvent::Ime(Ime::Commit(text)),
|
event: WindowEvent::Ime(Ime::Commit(text)),
|
||||||
@@ -1266,6 +1312,10 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
// trying receiving composing result and commit if exists.
|
// trying receiving composing result and commit if exists.
|
||||||
let ime_context = ImeContext::current(window);
|
let ime_context = ImeContext::current(window);
|
||||||
if let Some(text) = ime_context.get_composed_text() {
|
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 {
|
userdata.send_event(Event::WindowEvent {
|
||||||
window_id: RootWindowId(WindowId(window)),
|
window_id: RootWindowId(WindowId(window)),
|
||||||
event: WindowEvent::Ime(Ime::Commit(text)),
|
event: WindowEvent::Ime(Ime::Commit(text)),
|
||||||
@@ -1435,35 +1485,36 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
|
|
||||||
WM_KEYDOWN | WM_SYSKEYDOWN => {
|
WM_KEYDOWN | WM_SYSKEYDOWN => {
|
||||||
use crate::event::{ElementState::Pressed, VirtualKeyCode};
|
use crate::event::{ElementState::Pressed, VirtualKeyCode};
|
||||||
if msg == WM_SYSKEYDOWN && wparam == VK_F4 as usize {
|
if let Some((scancode, vkey)) = process_key_params(wparam, lparam) {
|
||||||
DefWindowProcW(window, msg, wparam, lparam)
|
update_modifiers(window, userdata);
|
||||||
} else {
|
|
||||||
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 {
|
userdata.send_event(Event::WindowEvent {
|
||||||
window_id: RootWindowId(WindowId(window)),
|
window_id: RootWindowId(WindowId(window)),
|
||||||
event: WindowEvent::KeyboardInput {
|
event: WindowEvent::ReceivedCharacter('\u{7F}'),
|
||||||
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::ReceivedCharacter('\u{7F}'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg == WM_SYSKEYDOWN {
|
||||||
|
DefWindowProcW(window, msg, wparam, lparam)
|
||||||
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1860,7 +1911,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
WM_NCACTIVATE => {
|
WM_NCACTIVATE => {
|
||||||
let is_active = wparam == 1;
|
let is_active = wparam != false.into();
|
||||||
let active_focus_changed = userdata.window_state.lock().set_active(is_active);
|
let active_focus_changed = userdata.window_state.lock().set_active(is_active);
|
||||||
if active_focus_changed {
|
if active_focus_changed {
|
||||||
if is_active {
|
if is_active {
|
||||||
@@ -1921,11 +1972,13 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
let mmi = lparam as *mut MINMAXINFO;
|
let mmi = lparam as *mut MINMAXINFO;
|
||||||
|
|
||||||
let window_state = userdata.window_state.lock();
|
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 window_state.min_size.is_some() || window_state.max_size.is_some() {
|
||||||
if let Some(min_size) = window_state.min_size {
|
if let Some(min_size) = window_state.min_size {
|
||||||
let min_size = min_size.to_physical(window_state.scale_factor);
|
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 {
|
(*mmi).ptMinTrackSize = POINT {
|
||||||
x: width as i32,
|
x: width as i32,
|
||||||
y: height as i32,
|
y: height as i32,
|
||||||
@@ -1933,7 +1986,8 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
}
|
}
|
||||||
if let Some(max_size) = window_state.max_size {
|
if let Some(max_size) = window_state.max_size {
|
||||||
let max_size = max_size.to_physical(window_state.scale_factor);
|
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 {
|
(*mmi).ptMaxTrackSize = POINT {
|
||||||
x: width as i32,
|
x: width as i32,
|
||||||
y: height as i32,
|
y: height as i32,
|
||||||
@@ -1957,7 +2011,7 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
let new_scale_factor = dpi_to_scale_factor(new_dpi_x);
|
let new_scale_factor = dpi_to_scale_factor(new_dpi_x);
|
||||||
let old_scale_factor: f64;
|
let old_scale_factor: f64;
|
||||||
|
|
||||||
let allow_resize = {
|
let (allow_resize, window_flags) = {
|
||||||
let mut window_state = userdata.window_state.lock();
|
let mut window_state = userdata.window_state.lock();
|
||||||
old_scale_factor = window_state.scale_factor;
|
old_scale_factor = window_state.scale_factor;
|
||||||
window_state.scale_factor = new_scale_factor;
|
window_state.scale_factor = new_scale_factor;
|
||||||
@@ -1966,12 +2020,11 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_state.fullscreen.is_none()
|
let allow_resize = window_state.fullscreen.is_none()
|
||||||
&& !window_state.window_flags().contains(WindowFlags::MAXIMIZED)
|
&& !window_state.window_flags().contains(WindowFlags::MAXIMIZED);
|
||||||
};
|
|
||||||
|
|
||||||
let style = GetWindowLongW(window, GWL_STYLE) as u32;
|
(allow_resize, window_state.window_flags)
|
||||||
let style_ex = GetWindowLongW(window, GWL_EXSTYLE) as u32;
|
};
|
||||||
|
|
||||||
// New size as suggested by Windows.
|
// New size as suggested by Windows.
|
||||||
let suggested_rect = *(lparam as *const RECT);
|
let suggested_rect = *(lparam as *const RECT);
|
||||||
@@ -1985,28 +2038,18 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
// let margin_right: i32;
|
// let margin_right: i32;
|
||||||
// let margin_bottom: i32;
|
// let margin_bottom: i32;
|
||||||
{
|
{
|
||||||
let adjusted_rect =
|
let adjusted_rect = window_flags
|
||||||
util::adjust_window_rect_with_styles(window, style, style_ex, suggested_rect)
|
.adjust_rect(window, suggested_rect)
|
||||||
.unwrap_or(suggested_rect);
|
.unwrap_or(suggested_rect);
|
||||||
margin_left = suggested_rect.left - adjusted_rect.left;
|
margin_left = suggested_rect.left - adjusted_rect.left;
|
||||||
margin_top = suggested_rect.top - adjusted_rect.top;
|
margin_top = suggested_rect.top - adjusted_rect.top;
|
||||||
// margin_right = adjusted_rect.right - suggested_rect.right;
|
// margin_right = adjusted_rect.right - suggested_rect.right;
|
||||||
// margin_bottom = adjusted_rect.bottom - suggested_rect.bottom;
|
// margin_bottom = adjusted_rect.bottom - suggested_rect.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
let old_physical_inner_rect = {
|
let old_physical_inner_rect = util::WindowArea::Inner
|
||||||
let mut old_physical_inner_rect = mem::zeroed();
|
.get_rect(window)
|
||||||
GetClientRect(window, &mut old_physical_inner_rect);
|
.expect("failed to query (old) inner window area");
|
||||||
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_size = PhysicalSize::new(
|
let old_physical_inner_size = PhysicalSize::new(
|
||||||
(old_physical_inner_rect.right - old_physical_inner_rect.left) as u32,
|
(old_physical_inner_rect.right - old_physical_inner_rect.left) as u32,
|
||||||
(old_physical_inner_rect.bottom - old_physical_inner_rect.top) as u32,
|
(old_physical_inner_rect.bottom - old_physical_inner_rect.top) as u32,
|
||||||
@@ -2060,13 +2103,9 @@ unsafe fn public_window_callback_inner<T: 'static>(
|
|||||||
bottom: suggested_ul.1 + new_physical_inner_size.height as i32,
|
bottom: suggested_ul.1 + new_physical_inner_size.height as i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
conservative_rect = util::adjust_window_rect_with_styles(
|
conservative_rect = window_flags
|
||||||
window,
|
.adjust_rect(window, conservative_rect)
|
||||||
style,
|
.unwrap_or(conservative_rect);
|
||||||
style_ex,
|
|
||||||
conservative_rect,
|
|
||||||
)
|
|
||||||
.unwrap_or(conservative_rect);
|
|
||||||
|
|
||||||
// If we're dragging the window, offset the window so that the cursor's
|
// If we're dragging the window, offset the window so that the cursor's
|
||||||
// relative horizontal position in the title bar is preserved.
|
// relative horizontal position in the title bar is preserved.
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ use crate::{
|
|||||||
dpi::PhysicalSize,
|
dpi::PhysicalSize,
|
||||||
event::{Event, StartCause, WindowEvent},
|
event::{Event, StartCause, WindowEvent},
|
||||||
event_loop::ControlFlow,
|
event_loop::ControlFlow,
|
||||||
platform_impl::platform::util,
|
platform_impl::platform::{
|
||||||
|
event_loop::{WindowData, GWL_USERDATA},
|
||||||
|
get_window_long,
|
||||||
|
},
|
||||||
window::WindowId,
|
window::WindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -434,11 +437,13 @@ impl<T> BufferedEvent<T> {
|
|||||||
new_inner_size: &mut new_inner_size,
|
new_inner_size: &mut new_inner_size,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
util::set_inner_size_physical(
|
|
||||||
(window_id.0).0,
|
let window_flags = unsafe {
|
||||||
new_inner_size.width as _,
|
let userdata =
|
||||||
new_inner_size.height as _,
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
|||||||
pub drag_and_drop: bool,
|
pub drag_and_drop: bool,
|
||||||
pub preferred_theme: Option<Theme>,
|
pub preferred_theme: Option<Theme>,
|
||||||
pub skip_taskbar: bool,
|
pub skip_taskbar: bool,
|
||||||
|
pub decoration_shadow: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PlatformSpecificWindowBuilderAttributes {
|
impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||||
@@ -48,6 +49,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
|
|||||||
drag_and_drop: true,
|
drag_and_drop: true,
|
||||||
preferred_theme: None,
|
preferred_theme: None,
|
||||||
skip_taskbar: false,
|
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 {
|
impl From<u64> for WindowId {
|
||||||
fn from(raw_id: u64) -> Self {
|
fn from(raw_id: u64) -> Self {
|
||||||
Self(raw_id as HWND)
|
Self(raw_id as HWND)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use windows_sys::{
|
|||||||
core::{HRESULT, PCWSTR},
|
core::{HRESULT, PCWSTR},
|
||||||
Win32::{
|
Win32::{
|
||||||
Foundation::{BOOL, HINSTANCE, HWND, RECT},
|
Foundation::{BOOL, HINSTANCE, HWND, RECT},
|
||||||
Graphics::Gdi::{ClientToScreen, InvalidateRgn, HMONITOR},
|
Graphics::Gdi::{ClientToScreen, HMONITOR},
|
||||||
System::{
|
System::{
|
||||||
LibraryLoader::{GetProcAddress, LoadLibraryA},
|
LibraryLoader::{GetProcAddress, LoadLibraryA},
|
||||||
SystemServices::IMAGE_DOS_HEADER,
|
SystemServices::IMAGE_DOS_HEADER,
|
||||||
@@ -23,19 +23,16 @@ use windows_sys::{
|
|||||||
HiDpi::{DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS},
|
HiDpi::{DPI_AWARENESS_CONTEXT, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS},
|
||||||
Input::KeyboardAndMouse::GetActiveWindow,
|
Input::KeyboardAndMouse::GetActiveWindow,
|
||||||
WindowsAndMessaging::{
|
WindowsAndMessaging::{
|
||||||
AdjustWindowRectEx, ClipCursor, GetClientRect, GetClipCursor, GetMenu,
|
ClipCursor, GetClientRect, GetClipCursor, GetSystemMetrics, GetWindowRect,
|
||||||
GetSystemMetrics, GetWindowLongW, GetWindowRect, SetWindowPos, ShowCursor,
|
ShowCursor, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP, IDC_IBEAM,
|
||||||
GWL_EXSTYLE, GWL_STYLE, IDC_APPSTARTING, IDC_ARROW, IDC_CROSS, IDC_HAND, IDC_HELP,
|
IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE, IDC_WAIT,
|
||||||
IDC_IBEAM, IDC_NO, IDC_SIZEALL, IDC_SIZENESW, IDC_SIZENS, IDC_SIZENWSE, IDC_SIZEWE,
|
SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN,
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{dpi::PhysicalSize, window::CursorIcon};
|
use crate::window::CursorIcon;
|
||||||
|
|
||||||
pub fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
|
pub fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
|
||||||
string.as_ref().encode_wide().chain(once(0)).collect()
|
string.as_ref().encode_wide().chain(once(0)).collect()
|
||||||
@@ -56,114 +53,43 @@ where
|
|||||||
bitset & flag == flag
|
bitset & flag == flag
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn status_map<T, F: FnMut(&mut T) -> BOOL>(mut fun: F) -> Option<T> {
|
pub(crate) fn win_to_err(result: BOOL) -> Result<(), io::Error> {
|
||||||
let mut data: T = mem::zeroed();
|
if result != false.into() {
|
||||||
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() {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(io::Error::last_os_error())
|
Err(io::Error::last_os_error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
|
pub enum WindowArea {
|
||||||
unsafe { status_map(|rect| GetWindowRect(hwnd, rect)) }
|
Outer,
|
||||||
|
Inner,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_client_rect(hwnd: HWND) -> Result<RECT, io::Error> {
|
impl WindowArea {
|
||||||
unsafe {
|
pub fn get_rect(self, hwnd: HWND) -> Result<RECT, io::Error> {
|
||||||
let mut rect = mem::zeroed();
|
let mut rect = unsafe { mem::zeroed() };
|
||||||
let mut top_left = mem::zeroed();
|
|
||||||
|
|
||||||
win_to_err(|| ClientToScreen(hwnd, &mut top_left))?;
|
match self {
|
||||||
win_to_err(|| GetClientRect(hwnd, &mut rect))?;
|
WindowArea::Outer => {
|
||||||
rect.left += top_left.x;
|
win_to_err(unsafe { GetWindowRect(hwnd, &mut rect) })?;
|
||||||
rect.top += top_left.y;
|
}
|
||||||
rect.right += top_left.x;
|
WindowArea::Inner => unsafe {
|
||||||
rect.bottom += top_left.y;
|
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)
|
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) {
|
pub fn set_cursor_hidden(hidden: bool) {
|
||||||
static HIDDEN: AtomicBool = AtomicBool::new(false);
|
static HIDDEN: AtomicBool = AtomicBool::new(false);
|
||||||
let changed = HIDDEN.swap(hidden, Ordering::SeqCst) ^ hidden;
|
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> {
|
pub fn get_cursor_clip() -> Result<RECT, io::Error> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut rect: RECT = mem::zeroed();
|
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()
|
.as_ref()
|
||||||
.map(|r| r as *const RECT)
|
.map(|r| r as *const RECT)
|
||||||
.unwrap_or(ptr::null());
|
.unwrap_or(ptr::null());
|
||||||
win_to_err(|| ClipCursor(rect_ptr))
|
win_to_err(ClipCursor(rect_ptr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ impl Window {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
|
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)))
|
.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")
|
.expect("Unexpected GetWindowRect failure; please report this error to https://github.com/rust-windowing/winit")
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,8 @@ impl Window {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
pub fn outer_size(&self) -> PhysicalSize<u32> {
|
||||||
util::get_window_rect(self.hwnd())
|
util::WindowArea::Outer
|
||||||
|
.get_rect(self.hwnd())
|
||||||
.map(|rect| {
|
.map(|rect| {
|
||||||
PhysicalSize::new(
|
PhysicalSize::new(
|
||||||
(rect.right - rect.left) as u32,
|
(rect.right - rect.left) as u32,
|
||||||
@@ -200,7 +201,7 @@ impl Window {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_inner_size(&self, size: Size) {
|
pub fn set_inner_size(&self, size: Size) {
|
||||||
let scale_factor = self.scale_factor();
|
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_state = Arc::clone(&self.window_state);
|
||||||
let window = self.window.clone();
|
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]
|
#[inline]
|
||||||
@@ -577,7 +579,7 @@ impl Window {
|
|||||||
self.thread_executor.execute_in_thread(move || {
|
self.thread_executor.execute_in_thread(move || {
|
||||||
let _ = &window;
|
let _ = &window;
|
||||||
WindowState::set_window_flags(window_state.lock(), window.0, |f| {
|
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]
|
#[inline]
|
||||||
pub fn is_decorated(&self) -> bool {
|
pub fn is_decorated(&self) -> bool {
|
||||||
let window_state = self.window_state.lock();
|
let window_state = self.window_state.lock();
|
||||||
window_state.window_flags.contains(WindowFlags::DECORATIONS)
|
window_state
|
||||||
|
.window_flags
|
||||||
|
.contains(WindowFlags::MARKER_DECORATIONS)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -691,6 +695,19 @@ impl Window {
|
|||||||
unsafe { set_skip_taskbar(self.hwnd(), skip) };
|
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]
|
#[inline]
|
||||||
pub fn focus_window(&self) {
|
pub fn focus_window(&self) {
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
@@ -898,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 {
|
if let Some(position) = attributes.position {
|
||||||
win.set_outer_position(position);
|
win.set_outer_position(position);
|
||||||
}
|
}
|
||||||
@@ -916,7 +941,11 @@ where
|
|||||||
let class_name = register_window_class::<T>(&attributes.window_icon, &pl_attribs.taskbar_icon);
|
let class_name = register_window_class::<T>(&attributes.window_icon, &pl_attribs.taskbar_icon);
|
||||||
|
|
||||||
let mut window_flags = WindowFlags::empty();
|
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::ALWAYS_ON_TOP, attributes.always_on_top);
|
||||||
window_flags.set(
|
window_flags.set(
|
||||||
WindowFlags::NO_BACK_BUFFER,
|
WindowFlags::NO_BACK_BUFFER,
|
||||||
@@ -997,6 +1026,7 @@ unsafe fn register_window_class<T: 'static>(
|
|||||||
.map(|icon| icon.inner.as_raw_handle())
|
.map(|icon| icon.inner.as_raw_handle())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
use windows_sys::Win32::UI::WindowsAndMessaging::COLOR_WINDOWFRAME;
|
||||||
let class = WNDCLASSEXW {
|
let class = WNDCLASSEXW {
|
||||||
cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
|
cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
|
||||||
style: CS_HREDRAW | CS_VREDRAW,
|
style: CS_HREDRAW | CS_VREDRAW,
|
||||||
@@ -1006,7 +1036,7 @@ unsafe fn register_window_class<T: 'static>(
|
|||||||
hInstance: util::get_instance_handle(),
|
hInstance: util::get_instance_handle(),
|
||||||
hIcon: h_icon,
|
hIcon: h_icon,
|
||||||
hCursor: 0, // must be null in order for cursor state to work properly
|
hCursor: 0, // must be null in order for cursor state to work properly
|
||||||
hbrBackground: 0,
|
hbrBackground: COLOR_WINDOWFRAME as _,
|
||||||
lpszMenuName: ptr::null(),
|
lpszMenuName: ptr::null(),
|
||||||
lpszClassName: class_name.as_ptr(),
|
lpszClassName: class_name.as_ptr(),
|
||||||
hIconSm: h_icon_small,
|
hIconSm: h_icon_small,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, Size},
|
dpi::{PhysicalPosition, PhysicalSize, Size},
|
||||||
event::ModifiersState,
|
event::ModifiersState,
|
||||||
icon::Icon,
|
icon::Icon,
|
||||||
platform_impl::platform::{event_loop, util},
|
platform_impl::platform::{event_loop, util},
|
||||||
@@ -11,14 +11,15 @@ use windows_sys::Win32::{
|
|||||||
Foundation::{HWND, RECT},
|
Foundation::{HWND, RECT},
|
||||||
Graphics::Gdi::InvalidateRgn,
|
Graphics::Gdi::InvalidateRgn,
|
||||||
UI::WindowsAndMessaging::{
|
UI::WindowsAndMessaging::{
|
||||||
SendMessageW, SetWindowLongW, SetWindowPos, ShowWindow, GWL_EXSTYLE, GWL_STYLE,
|
AdjustWindowRectEx, GetMenu, GetWindowLongW, SendMessageW, SetWindowLongW, SetWindowPos,
|
||||||
HWND_NOTOPMOST, HWND_TOPMOST, SWP_ASYNCWINDOWPOS, SWP_FRAMECHANGED, SWP_NOACTIVATE,
|
ShowWindow, GWL_EXSTYLE, GWL_STYLE, HWND_NOTOPMOST, HWND_TOPMOST, SWP_ASYNCWINDOWPOS,
|
||||||
SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE,
|
SWP_FRAMECHANGED, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOREPOSITION, SWP_NOSIZE, SWP_NOZORDER,
|
||||||
SW_SHOW, WINDOWPLACEMENT, WINDOW_EX_STYLE, WINDOW_STYLE, WS_BORDER, WS_CAPTION, WS_CHILD,
|
SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOW, WINDOWPLACEMENT, WINDOW_EX_STYLE,
|
||||||
WS_CLIPCHILDREN, WS_CLIPSIBLINGS, WS_EX_ACCEPTFILES, WS_EX_APPWINDOW, WS_EX_LAYERED,
|
WINDOW_STYLE, WS_BORDER, WS_CAPTION, WS_CHILD, WS_CLIPCHILDREN, WS_CLIPSIBLINGS,
|
||||||
WS_EX_LEFT, WS_EX_NOREDIRECTIONBITMAP, WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_EX_WINDOWEDGE,
|
WS_EX_ACCEPTFILES, WS_EX_APPWINDOW, WS_EX_LAYERED, WS_EX_NOREDIRECTIONBITMAP,
|
||||||
WS_MAXIMIZE, WS_MAXIMIZEBOX, WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPED,
|
WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_EX_WINDOWEDGE, WS_MAXIMIZE, WS_MAXIMIZEBOX,
|
||||||
WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SIZEBOX, WS_SYSMENU, WS_VISIBLE,
|
WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SIZEBOX, WS_SYSMENU,
|
||||||
|
WS_VISIBLE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,36 +77,39 @@ bitflags! {
|
|||||||
bitflags! {
|
bitflags! {
|
||||||
pub struct WindowFlags: u32 {
|
pub struct WindowFlags: u32 {
|
||||||
const RESIZABLE = 1 << 0;
|
const RESIZABLE = 1 << 0;
|
||||||
const DECORATIONS = 1 << 1;
|
const VISIBLE = 1 << 1;
|
||||||
const VISIBLE = 1 << 2;
|
const ON_TASKBAR = 1 << 2;
|
||||||
const ON_TASKBAR = 1 << 3;
|
const ALWAYS_ON_TOP = 1 << 3;
|
||||||
const ALWAYS_ON_TOP = 1 << 4;
|
const NO_BACK_BUFFER = 1 << 4;
|
||||||
const NO_BACK_BUFFER = 1 << 5;
|
const TRANSPARENT = 1 << 5;
|
||||||
const TRANSPARENT = 1 << 6;
|
const CHILD = 1 << 6;
|
||||||
const CHILD = 1 << 7;
|
const MAXIMIZED = 1 << 7;
|
||||||
const MAXIMIZED = 1 << 8;
|
const POPUP = 1 << 8;
|
||||||
const POPUP = 1 << 14;
|
|
||||||
|
|
||||||
/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
|
/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
|
||||||
/// included here to make masking easier.
|
/// included here to make masking easier.
|
||||||
const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 9;
|
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`.
|
/// 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
|
/// 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
|
/// 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
|
/// 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.
|
/// 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 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) {
|
if self.contains(WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN) {
|
||||||
self |= WindowFlags::EXCLUSIVE_FULLSCREEN_OR_MASK;
|
self |= WindowFlags::EXCLUSIVE_FULLSCREEN_OR_MASK;
|
||||||
}
|
}
|
||||||
if !self.contains(WindowFlags::DECORATIONS) {
|
|
||||||
self &= WindowFlags::NO_DECORATIONS_AND_MASK;
|
|
||||||
}
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_window_styles(self) -> (WINDOW_STYLE, WINDOW_EX_STYLE) {
|
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) {
|
if self.contains(WindowFlags::RESIZABLE) {
|
||||||
style |= WS_SIZEBOX | WS_MAXIMIZEBOX;
|
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) {
|
if self.contains(WindowFlags::VISIBLE) {
|
||||||
style |= WS_VISIBLE;
|
style |= WS_VISIBLE;
|
||||||
}
|
}
|
||||||
@@ -272,9 +276,6 @@ impl WindowFlags {
|
|||||||
style_ex |= WS_EX_TRANSPARENT | WS_EX_LAYERED;
|
style_ex |= WS_EX_TRANSPARENT | WS_EX_LAYERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU;
|
|
||||||
style_ex |= WS_EX_ACCEPTFILES;
|
|
||||||
|
|
||||||
if self.intersects(
|
if self.intersects(
|
||||||
WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN | WindowFlags::MARKER_BORDERLESS_FULLSCREEN,
|
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 {
|
impl CursorFlags {
|
||||||
fn refresh_os_cursor(self, window: HWND) -> Result<(), io::Error> {
|
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) {
|
if util::is_focused(window) {
|
||||||
let cursor_clip = match self.contains(CursorFlags::GRABBED) {
|
let cursor_clip = match self.contains(CursorFlags::GRABBED) {
|
||||||
|
|||||||
Reference in New Issue
Block a user