mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 07:03:15 -04:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6db308f1e9 | ||
|
|
6f70fd90b9 | ||
|
|
db038d943c | ||
|
|
c5620efc9c | ||
|
|
8fb7aa5cef | ||
|
|
6ddee9a8ac | ||
|
|
5700359a61 | ||
|
|
0861a353d6 | ||
|
|
f79efec7ef | ||
|
|
77d5d20391 | ||
|
|
165e51d850 | ||
|
|
1c38f113b3 | ||
|
|
66859607a3 | ||
|
|
edf396b1a4 | ||
|
|
cbeb51b436 | ||
|
|
45e4fd6ec1 | ||
|
|
3a077ff211 | ||
|
|
be850e483a | ||
|
|
33fb62bb25 | ||
|
|
66c117e599 | ||
|
|
8aa1be8336 | ||
|
|
037d4121a1 | ||
|
|
fbd3918d3a | ||
|
|
7c543a43a9 | ||
|
|
ee3996cac6 | ||
|
|
96809ac659 | ||
|
|
6343059bc0 | ||
|
|
5a78fe33e8 | ||
|
|
676fb947f2 |
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,4 +1,30 @@
|
||||
# Unreleased
|
||||
# 0.24.0 (2020-12-09)
|
||||
|
||||
- On Windows, fix applications not exiting gracefully due to thread_event_target_callback accessing corrupted memory.
|
||||
- On Windows, implement `Window::set_ime_position`.
|
||||
- **Breaking:** On Windows, Renamed `WindowBuilderExtWindows`'s `is_dark_mode` to `theme`.
|
||||
- On Windows, add `WindowBuilderExtWindows::with_theme` to set a preferred theme.
|
||||
- On Windows, fix bug causing message boxes to appear delayed.
|
||||
- On Android, calling `WindowEvent::Focused` now works properly instead of always returning false.
|
||||
- On Windows, fix alt-tab behaviour by removing borderless fullscreen "always on top" flag.
|
||||
- On Windows, fix bug preventing windows with transparency enabled from having fully-opaque regions.
|
||||
- **Breaking:** On Windows, include prefix byte in scancodes.
|
||||
- On Wayland, fix window not being resizeable when using `with_min_inner_size` in `WindowBuilder`.
|
||||
- On Unix, fix cross-compiling to wasm32 without enabling X11 or Wayland.
|
||||
- On Windows, fix use after free crash during window destruction.
|
||||
- On Web, fix `WindowEvent::ReceivedCharacter` never being sent on key input.
|
||||
- On macOS, fix compilation when targeting aarch64
|
||||
- On X11, fix `Window::request_redraw` not waking the event loop.
|
||||
- On Wayland, the keypad arrow keys are now recognized.
|
||||
- **Breaking** Rename `desktop::EventLoopExtDesktop` to `run_return::EventLoopExtRunReturn`.
|
||||
- Added `request_user_attention` method to `Window`.
|
||||
- **Breaking:** On macOS, removed `WindowExt::request_user_attention`, use `Window::request_user_attention`.
|
||||
- **Breaking:** On X11, removed `WindowExt::set_urgent`, use `Window::request_user_attention`.
|
||||
- On Wayland, default font size in CSD increased from 11 to 17.
|
||||
- On Windows, fix bug causing message boxes to appear delayed.
|
||||
- On Android, support multi-touch.
|
||||
- On Wayland, extra mouse buttons are not dropped anymore.
|
||||
- **Breaking**: `MouseButton::Other` now uses `u16`.
|
||||
|
||||
# 0.23.0 (2020-10-02)
|
||||
|
||||
|
||||
27
Cargo.toml
27
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.23.0"
|
||||
version = "0.24.0"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2018"
|
||||
@@ -20,7 +20,7 @@ targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "i686-unknown-linux
|
||||
default = ["x11", "wayland"]
|
||||
web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"]
|
||||
stdweb = ["std_web", "instant/stdweb"]
|
||||
x11 = ["x11-dl"]
|
||||
x11 = ["x11-dl", "mio", "mio-extras", "percent-encoding", "parking_lot"]
|
||||
wayland = ["wayland-client", "sctk"]
|
||||
|
||||
[dependencies]
|
||||
@@ -33,7 +33,7 @@ raw-window-handle = "0.3"
|
||||
bitflags = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
image = "0.23"
|
||||
image = "0.23.12"
|
||||
simple_logger = "1.9"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
@@ -41,21 +41,23 @@ ndk = "0.2.0"
|
||||
ndk-sys = "0.2.0"
|
||||
ndk-glue = "0.2.0"
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
objc = "0.2.3"
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
objc = "0.2.7"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa = "0.23"
|
||||
cocoa = "0.24"
|
||||
core-foundation = "0.9"
|
||||
core-graphics = "0.22"
|
||||
dispatch = "0.2.0"
|
||||
objc = "0.2.6"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
|
||||
version = "0.1.4"
|
||||
default_features = false
|
||||
features = ["display_link"]
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
parking_lot = "0.11"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3.6"
|
||||
features = [
|
||||
@@ -63,6 +65,7 @@ features = [
|
||||
"commctrl",
|
||||
"dwmapi",
|
||||
"errhandlingapi",
|
||||
"imm",
|
||||
"hidusage",
|
||||
"libloaderapi",
|
||||
"objbase",
|
||||
@@ -83,13 +86,11 @@ features = [
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.28", features = [ "dlopen"] , optional = true }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.12", optional = true }
|
||||
mio = "0.6"
|
||||
mio-extras = "2.0"
|
||||
mio = { version = "0.6", optional = true }
|
||||
mio-extras = { version = "2.0", optional = true }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
percent-encoding = "2.0"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies.parking_lot]
|
||||
version = "0.11"
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
parking_lot = { version = "0.11.0", optional = true }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.web_sys]
|
||||
package = "web-sys"
|
||||
|
||||
@@ -117,7 +117,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Setting the taskbar icon
|
||||
* Setting the parent window
|
||||
* `WS_EX_NOREDIRECTIONBITMAP` support
|
||||
* Theme the title bar according to Windows 10 Dark Mode setting
|
||||
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
|
||||
|
||||
### macOS
|
||||
* Window activation policy
|
||||
@@ -202,7 +202,7 @@ Legend:
|
||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |❌ |
|
||||
|Multitouch |✔️ |❌ |✔️ |✔️ |❓ |✔️ |❌ |
|
||||
|Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |
|
||||
|Keyboard events |✔️ |✔️ |✔️ |✔️ |❓ |❌ |✔️ |
|
||||
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |❌[#306] |**N/A**|**N/A**|❓ |
|
||||
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.23.0"
|
||||
winit = "0.24.0"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
21
build.rs
21
build.rs
@@ -1,21 +0,0 @@
|
||||
#[cfg(all(
|
||||
any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
),
|
||||
not(feature = "x11"),
|
||||
not(feature = "wayland")
|
||||
))]
|
||||
compile_error!("at least one of the \"x11\"/\"wayland\" features must be enabled");
|
||||
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
not(feature = "web-sys"),
|
||||
not(feature = "stdweb")
|
||||
))]
|
||||
compile_error!("at least one of the \"web-sys\"/\"stdweb\" features must be enabled");
|
||||
|
||||
fn main() {}
|
||||
54
examples/set_ime_position.rs
Normal file
54
examples/set_ime_position.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::PhysicalPosition,
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
println!("Ime position will system default");
|
||||
println!("Click to set ime position to cursor's");
|
||||
|
||||
let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CursorMoved { position, .. },
|
||||
..
|
||||
} => {
|
||||
cursor_position = position;
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
println!(
|
||||
"Setting ime position to {}, {}",
|
||||
cursor_position.x, cursor_position.y
|
||||
);
|
||||
window.set_ime_position(cursor_position);
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -49,7 +49,7 @@ fn load_icon(path: &Path) -> Icon {
|
||||
let (icon_rgba, icon_width, icon_height) = {
|
||||
let image = image::open(path)
|
||||
.expect("Failed to open icon path")
|
||||
.into_rgba();
|
||||
.into_rgba8();
|
||||
let (width, height) = image.dimensions();
|
||||
let rgba = image.into_raw();
|
||||
(rgba, width, height)
|
||||
|
||||
@@ -15,7 +15,7 @@ fn main() {
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
platform::desktop::EventLoopExtDesktop,
|
||||
platform::run_return::EventLoopExtRunReturn,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
let mut event_loop = EventLoop::new();
|
||||
|
||||
@@ -745,7 +745,7 @@ pub enum MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
Other(u8),
|
||||
Other(u16),
|
||||
}
|
||||
|
||||
/// Describes a difference in the mouse scroll wheel state.
|
||||
|
||||
12
src/lib.rs
12
src/lib.rs
@@ -35,10 +35,10 @@
|
||||
//! entire program terminates.
|
||||
//!
|
||||
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
|
||||
//! model, since that can't be implemented properly on web and mobile platforms and works poorly on
|
||||
//! most desktop platforms. However, this model can be re-implemented to an extent on desktops with
|
||||
//! [`EventLoopExtDesktop::run_return`]. See that method's documentation for more reasons about why
|
||||
//! it's discouraged, beyond mobile/web compatibility reasons.
|
||||
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
|
||||
//! most other platforms. However, this model can be re-implemented to an extent with
|
||||
//! [`EventLoopExtRunReturn::run_return`]. See that method's documentation for more reasons about why
|
||||
//! it's discouraged, beyond compatibility reasons.
|
||||
//!
|
||||
//!
|
||||
//! ```no_run
|
||||
@@ -109,7 +109,7 @@
|
||||
//! window visible only once you're ready to render into it.
|
||||
//!
|
||||
//! [`EventLoop`]: event_loop::EventLoop
|
||||
//! [`EventLoopExtDesktop::run_return`]: ./platform/desktop/trait.EventLoopExtDesktop.html#tymethod.run_return
|
||||
//! [`EventLoopExtRunReturn::run_return`]: ./platform/run_return/trait.EventLoopExtRunReturn.html#tymethod.run_return
|
||||
//! [`EventLoop::new()`]: event_loop::EventLoop::new
|
||||
//! [event_loop_run]: event_loop::EventLoop::run
|
||||
//! [`ControlFlow`]: event_loop::ControlFlow
|
||||
@@ -130,7 +130,7 @@
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![deny(broken_intra_doc_links)]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
|
||||
@@ -9,26 +9,6 @@ use crate::{
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
/// Corresponds to `NSRequestUserAttentionType`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum RequestUserAttentionType {
|
||||
/// Corresponds to `NSCriticalRequest`.
|
||||
///
|
||||
/// Dock icon will bounce until the application is focused.
|
||||
Critical,
|
||||
|
||||
/// Corresponds to `NSInformationalRequest`.
|
||||
///
|
||||
/// Dock icon will bounce once.
|
||||
Informational,
|
||||
}
|
||||
|
||||
impl Default for RequestUserAttentionType {
|
||||
fn default() -> Self {
|
||||
RequestUserAttentionType::Critical
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to MacOS.
|
||||
pub trait WindowExtMacOS {
|
||||
/// Returns a pointer to the cocoa `NSWindow` that is used by this window.
|
||||
@@ -41,10 +21,6 @@ pub trait WindowExtMacOS {
|
||||
/// The pointer will become invalid when the `Window` is destroyed.
|
||||
fn ns_view(&self) -> *mut c_void;
|
||||
|
||||
/// Request user attention, causing the application's dock icon to bounce.
|
||||
/// Note that this has no effect if the application is already focused.
|
||||
fn request_user_attention(&self, request_type: RequestUserAttentionType);
|
||||
|
||||
/// Returns whether or not the window is in simple fullscreen mode.
|
||||
fn simple_fullscreen(&self) -> bool;
|
||||
|
||||
@@ -75,11 +51,6 @@ impl WindowExtMacOS for Window {
|
||||
self.window.ns_view()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn request_user_attention(&self, request_type: RequestUserAttentionType) {
|
||||
self.window.request_user_attention(request_type)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simple_fullscreen(&self) -> bool {
|
||||
self.window.simple_fullscreen()
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
//!
|
||||
//! And the following platform-specific module:
|
||||
//!
|
||||
//! - `desktop` (available on `windows`, `unix`, and `macos`)
|
||||
//! - `run_return` (available on `windows`, `unix`, `macos`, and `android`)
|
||||
//!
|
||||
//! However only the module corresponding to the platform you're compiling to will be available.
|
||||
|
||||
@@ -21,5 +21,5 @@ pub mod macos;
|
||||
pub mod unix;
|
||||
pub mod windows;
|
||||
|
||||
pub mod desktop;
|
||||
pub mod run_return;
|
||||
pub mod web;
|
||||
|
||||
@@ -14,8 +14,8 @@ use crate::{
|
||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||
};
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to desktop platforms.
|
||||
pub trait EventLoopExtDesktop {
|
||||
/// Additional methods on `EventLoop` to return control flow to the caller.
|
||||
pub trait EventLoopExtRunReturn {
|
||||
/// A type provided by the user that can be passed through `Event::UserEvent`.
|
||||
type UserEvent;
|
||||
|
||||
@@ -25,12 +25,12 @@ pub trait EventLoopExtDesktop {
|
||||
/// control flow to the caller when `control_flow` is set to `ControlFlow::Exit`.
|
||||
///
|
||||
/// # Caveats
|
||||
/// Despite its apperance at first glance, this is *not* a perfect replacement for
|
||||
/// Despite its appearance at first glance, this is *not* a perfect replacement for
|
||||
/// `poll_events`. For example, this function will not return on Windows or macOS while a
|
||||
/// window is getting resized, resulting in all application logic outside of the
|
||||
/// `event_handler` closure not running until the resize operation ends. Other OS operations
|
||||
/// may also result in such freezes. This behavior is caused by fundamental limitations in the
|
||||
/// underyling OS APIs, which cannot be hidden by Winit without severe stability reprecussions.
|
||||
/// underlying OS APIs, which cannot be hidden by `winit` without severe stability repercussions.
|
||||
///
|
||||
/// You are strongly encouraged to use `run`, unless the use of this is absolutely necessary.
|
||||
fn run_return<F>(&mut self, event_handler: F)
|
||||
@@ -42,7 +42,7 @@ pub trait EventLoopExtDesktop {
|
||||
);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtDesktop for EventLoop<T> {
|
||||
impl<T> EventLoopExtRunReturn for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn run_return<F>(&mut self, event_handler: F)
|
||||
@@ -213,10 +213,6 @@ pub trait WindowExtUnix {
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
|
||||
/// Set window urgency hint (`XUrgencyHint`). Only relevant on X.
|
||||
#[cfg(feature = "x11")]
|
||||
fn set_urgent(&self, is_urgent: bool);
|
||||
|
||||
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
@@ -297,16 +293,6 @@ impl WindowExtUnix for Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn set_urgent(&self, is_urgent: bool) {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => w.set_urgent(is_urgent),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn xcb_connection(&self) -> Option<*mut raw::c_void> {
|
||||
@@ -486,13 +472,13 @@ pub trait Theme: Send + 'static {
|
||||
|
||||
/// Font name and the size for the title bar.
|
||||
///
|
||||
/// By default the font is `sans-serif` at the size of 11.
|
||||
/// By default the font is `sans-serif` at the size of 17.
|
||||
///
|
||||
/// Returning `None` means that title won't be drawn.
|
||||
fn font(&self) -> Option<(String, f32)> {
|
||||
// Not having any title isn't something desirable for the users, so setting it to
|
||||
// something generic.
|
||||
Some((String::from("sans-serif"), 11.))
|
||||
Some((String::from("sans-serif"), 17.))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{
|
||||
event_loop::EventLoop,
|
||||
monitor::MonitorHandle,
|
||||
platform_impl::{EventLoop as WindowsEventLoop, WinIcon},
|
||||
window::{BadIcon, Icon, Window, WindowBuilder},
|
||||
window::{BadIcon, Icon, Theme, Window, WindowBuilder},
|
||||
};
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to Windows.
|
||||
@@ -81,8 +81,8 @@ pub trait WindowExtWindows {
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
|
||||
|
||||
/// Whether the system theme is currently Windows 10's "Dark Mode".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
/// Returns the current window theme.
|
||||
fn theme(&self) -> Theme;
|
||||
}
|
||||
|
||||
impl WindowExtWindows for Window {
|
||||
@@ -102,8 +102,8 @@ impl WindowExtWindows for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_dark_mode(&self) -> bool {
|
||||
self.window.is_dark_mode()
|
||||
fn theme(&self) -> Theme {
|
||||
self.window.theme()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,9 @@ pub trait WindowBuilderExtWindows {
|
||||
/// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
|
||||
/// See https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks for more information.
|
||||
fn with_drag_and_drop(self, flag: bool) -> WindowBuilder;
|
||||
|
||||
/// Forces a theme or uses the system settings if `None` was provided.
|
||||
fn with_theme(self, theme: Option<Theme>) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtWindows for WindowBuilder {
|
||||
@@ -151,6 +154,12 @@ impl WindowBuilderExtWindows for WindowBuilder {
|
||||
self.platform_specific.drag_and_drop = flag;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_theme(mut self, theme: Option<Theme>) -> WindowBuilder {
|
||||
self.platform_specific.preferred_theme = theme;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorHandle` that are specific to Windows.
|
||||
|
||||
@@ -148,49 +148,91 @@ impl<T: 'static> EventLoop<T> {
|
||||
);
|
||||
}
|
||||
}
|
||||
Event::WindowHasFocus => {
|
||||
call_event_handler!(
|
||||
event_handler,
|
||||
self.window_target(),
|
||||
control_flow,
|
||||
event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Focused(true),
|
||||
}
|
||||
);
|
||||
}
|
||||
Event::WindowLostFocus => {
|
||||
call_event_handler!(
|
||||
event_handler,
|
||||
self.window_target(),
|
||||
control_flow,
|
||||
event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Focused(false),
|
||||
}
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Some(EventSource::InputQueue) => {
|
||||
if let Some(input_queue) = ndk_glue::input_queue().as_ref() {
|
||||
while let Some(event) = input_queue.get_event() {
|
||||
println!("event {:?}", event);
|
||||
if let Some(event) = input_queue.pre_dispatch(event) {
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let device_id = event::DeviceId(DeviceId);
|
||||
match &event {
|
||||
InputEvent::MotionEvent(motion_event) => {
|
||||
let phase = match motion_event.action() {
|
||||
MotionAction::Down => Some(event::TouchPhase::Started),
|
||||
MotionAction::Up => Some(event::TouchPhase::Ended),
|
||||
MotionAction::Down | MotionAction::PointerDown => {
|
||||
Some(event::TouchPhase::Started)
|
||||
}
|
||||
MotionAction::Up | MotionAction::PointerUp => {
|
||||
Some(event::TouchPhase::Ended)
|
||||
}
|
||||
MotionAction::Move => Some(event::TouchPhase::Moved),
|
||||
MotionAction::Cancel => {
|
||||
Some(event::TouchPhase::Cancelled)
|
||||
}
|
||||
_ => None, // TODO mouse events
|
||||
};
|
||||
let pointer = motion_event.pointer_at_index(0);
|
||||
let location = PhysicalPosition {
|
||||
x: pointer.x() as _,
|
||||
y: pointer.y() as _,
|
||||
};
|
||||
|
||||
if let Some(phase) = phase {
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id,
|
||||
event: event::WindowEvent::Touch(event::Touch {
|
||||
device_id,
|
||||
phase,
|
||||
location,
|
||||
id: 0,
|
||||
force: None,
|
||||
}),
|
||||
let pointers: Box<
|
||||
dyn Iterator<Item = ndk::event::Pointer<'_>>,
|
||||
> = match phase {
|
||||
event::TouchPhase::Started
|
||||
| event::TouchPhase::Ended => Box::new(
|
||||
std::iter::once(motion_event.pointer_at_index(
|
||||
motion_event.pointer_index(),
|
||||
)),
|
||||
),
|
||||
event::TouchPhase::Moved
|
||||
| event::TouchPhase::Cancelled => {
|
||||
Box::new(motion_event.pointers())
|
||||
}
|
||||
};
|
||||
call_event_handler!(
|
||||
event_handler,
|
||||
self.window_target(),
|
||||
control_flow,
|
||||
event
|
||||
);
|
||||
|
||||
for pointer in pointers {
|
||||
let location = PhysicalPosition {
|
||||
x: pointer.x() as _,
|
||||
y: pointer.y() as _,
|
||||
};
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id,
|
||||
event: event::WindowEvent::Touch(
|
||||
event::Touch {
|
||||
device_id,
|
||||
phase,
|
||||
location,
|
||||
id: pointer.pointer_id() as u64,
|
||||
force: None,
|
||||
},
|
||||
),
|
||||
};
|
||||
call_event_handler!(
|
||||
event_handler,
|
||||
self.window_target(),
|
||||
control_flow,
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
InputEvent::KeyEvent(_) => {} // TODO
|
||||
@@ -463,6 +505,8 @@ impl Window {
|
||||
|
||||
pub fn set_ime_position(&self, _position: Position) {}
|
||||
|
||||
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
|
||||
|
||||
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
|
||||
|
||||
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
|
||||
@@ -483,7 +527,7 @@ impl Window {
|
||||
let a_native_window = if let Some(native_window) = ndk_glue::native_window().as_ref() {
|
||||
unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ }
|
||||
} else {
|
||||
panic!("native window null");
|
||||
panic!("Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.");
|
||||
};
|
||||
let mut handle = raw_window_handle::android::AndroidHandle::empty();
|
||||
handle.a_native_window = a_native_window;
|
||||
|
||||
@@ -22,7 +22,9 @@ use crate::{
|
||||
},
|
||||
monitor, view, EventLoopWindowTarget, MonitorHandle,
|
||||
},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId},
|
||||
window::{
|
||||
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct Inner {
|
||||
@@ -260,6 +262,10 @@ impl Inner {
|
||||
warn!("`Window::set_ime_position` is ignored on iOS")
|
||||
}
|
||||
|
||||
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
|
||||
warn!("`Window::request_user_attention` is ignored on iOS")
|
||||
}
|
||||
|
||||
// Allow directly accessing the current monitor internally without unwrapping.
|
||||
fn current_monitor_inner(&self) -> RootMonitorHandle {
|
||||
unsafe {
|
||||
|
||||
@@ -30,7 +30,7 @@ use crate::{
|
||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
icon::Icon,
|
||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
||||
window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes},
|
||||
};
|
||||
|
||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||
@@ -418,6 +418,16 @@ impl Window {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_position(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
|
||||
match self {
|
||||
#[cfg(feature = "x11")]
|
||||
&Window::X(ref w) => w.request_user_attention(_request_type),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
x11_or_wayland!(match self; Window(w) => w.request_redraw())
|
||||
|
||||
@@ -149,6 +149,10 @@ pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
keysyms::XKB_KEY_KP_Page_Down => Some(VirtualKeyCode::PageDown),
|
||||
keysyms::XKB_KEY_KP_Home => Some(VirtualKeyCode::Home),
|
||||
keysyms::XKB_KEY_KP_End => Some(VirtualKeyCode::End),
|
||||
keysyms::XKB_KEY_KP_Left => Some(VirtualKeyCode::Left),
|
||||
keysyms::XKB_KEY_KP_Up => Some(VirtualKeyCode::Up),
|
||||
keysyms::XKB_KEY_KP_Right => Some(VirtualKeyCode::Right),
|
||||
keysyms::XKB_KEY_KP_Down => Some(VirtualKeyCode::Down),
|
||||
// => Some(VirtualKeyCode::OEM102),
|
||||
keysyms::XKB_KEY_period => Some(VirtualKeyCode::Period),
|
||||
// => Some(VirtualKeyCode::Playpause),
|
||||
|
||||
@@ -17,6 +17,11 @@ use crate::platform_impl::wayland::{self, DeviceId};
|
||||
|
||||
use super::{PointerData, WinitPointer};
|
||||
|
||||
// These values are comming from <linux/input-event-codes.h>.
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
const BTN_RIGHT: u32 = 0x111;
|
||||
const BTN_MIDDLE: u32 = 0x112;
|
||||
|
||||
#[inline]
|
||||
pub(super) fn handle_pointer(
|
||||
pointer: ThemedPointer,
|
||||
@@ -153,11 +158,10 @@ pub(super) fn handle_pointer(
|
||||
};
|
||||
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO - figure out the translation.
|
||||
_ => return,
|
||||
BTN_LEFT => MouseButton::Left,
|
||||
BTN_RIGHT => MouseButton::Right,
|
||||
BTN_MIDDLE => MouseButton::Middle,
|
||||
button => MouseButton::Other(button as u16),
|
||||
};
|
||||
|
||||
event_sink.push_window_event(
|
||||
|
||||
@@ -145,7 +145,7 @@ impl Window {
|
||||
|
||||
// Max dimensions.
|
||||
let max_size = attributes
|
||||
.min_inner_size
|
||||
.max_inner_size
|
||||
.map(|size| size.to_logical::<f64>(scale_factor as f64).into());
|
||||
window.set_max_size(max_size);
|
||||
|
||||
|
||||
@@ -709,7 +709,7 @@ impl<T: 'static> EventProcessor<T> {
|
||||
event: MouseInput {
|
||||
device_id,
|
||||
state,
|
||||
button: Other(x as u8),
|
||||
button: Other(x as u16),
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -32,7 +32,7 @@ use std::{
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice,
|
||||
sync::{mpsc, Arc, Mutex, Weak},
|
||||
sync::{mpsc, Arc, Weak},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
@@ -58,6 +58,7 @@ use crate::{
|
||||
|
||||
const X_TOKEN: Token = Token(0);
|
||||
const USER_TOKEN: Token = Token(1);
|
||||
const REDRAW_TOKEN: Token = Token(2);
|
||||
|
||||
pub struct EventLoopWindowTarget<T> {
|
||||
xconn: Arc<XConnection>,
|
||||
@@ -67,13 +68,14 @@ pub struct EventLoopWindowTarget<T> {
|
||||
root: ffi::Window,
|
||||
ime: RefCell<Ime>,
|
||||
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
||||
pending_redraws: Arc<Mutex<HashSet<WindowId>>>,
|
||||
redraw_sender: Sender<WindowId>,
|
||||
_marker: ::std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
poll: Poll,
|
||||
event_processor: EventProcessor<T>,
|
||||
redraw_channel: Receiver<WindowId>,
|
||||
user_channel: Receiver<T>,
|
||||
user_sender: Sender<T>,
|
||||
target: Rc<RootELW<T>>,
|
||||
@@ -174,32 +176,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
xconn.update_cached_wm_info(root);
|
||||
|
||||
let pending_redraws: Arc<Mutex<HashSet<WindowId>>> = Default::default();
|
||||
|
||||
let mut mod_keymap = ModifierKeymap::new();
|
||||
mod_keymap.reset_from_x_connection(&xconn);
|
||||
|
||||
let target = Rc::new(RootELW {
|
||||
p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
|
||||
ime,
|
||||
root,
|
||||
windows: Default::default(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
ime_sender,
|
||||
xconn,
|
||||
wm_delete_window,
|
||||
net_wm_ping,
|
||||
pending_redraws: pending_redraws.clone(),
|
||||
}),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
});
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
|
||||
let (user_sender, user_channel) = channel();
|
||||
let (redraw_sender, redraw_channel) = channel();
|
||||
|
||||
poll.register(
|
||||
&EventedFd(&get_xtarget(&target).xconn.x11_fd),
|
||||
&EventedFd(&xconn.x11_fd),
|
||||
X_TOKEN,
|
||||
Ready::readable(),
|
||||
PollOpt::level(),
|
||||
@@ -214,6 +200,29 @@ impl<T: 'static> EventLoop<T> {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
poll.register(
|
||||
&redraw_channel,
|
||||
REDRAW_TOKEN,
|
||||
Ready::readable(),
|
||||
PollOpt::level(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let target = Rc::new(RootELW {
|
||||
p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
|
||||
ime,
|
||||
root,
|
||||
windows: Default::default(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
ime_sender,
|
||||
xconn,
|
||||
wm_delete_window,
|
||||
net_wm_ping,
|
||||
redraw_sender,
|
||||
}),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
});
|
||||
|
||||
let event_processor = EventProcessor {
|
||||
target: target.clone(),
|
||||
dnd,
|
||||
@@ -239,6 +248,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
let result = EventLoop {
|
||||
poll,
|
||||
redraw_channel,
|
||||
user_channel,
|
||||
user_sender,
|
||||
event_processor,
|
||||
@@ -277,8 +287,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
// Process all pending events
|
||||
self.drain_events(&mut callback, &mut control_flow);
|
||||
|
||||
let wt = get_xtarget(&self.target);
|
||||
|
||||
// Empty the user event buffer
|
||||
{
|
||||
while let Ok(event) = self.user_channel.try_recv() {
|
||||
@@ -301,12 +309,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
// Empty the redraw requests
|
||||
{
|
||||
// Release the lock to prevent deadlock
|
||||
let windows: Vec<_> = wt.pending_redraws.lock().unwrap().drain().collect();
|
||||
let mut windows = HashSet::new();
|
||||
|
||||
for wid in windows {
|
||||
while let Ok(window_id) = self.redraw_channel.try_recv() {
|
||||
windows.insert(window_id);
|
||||
}
|
||||
|
||||
for window_id in windows {
|
||||
let window_id = crate::window::WindowId(super::WindowId::X(window_id));
|
||||
sticky_exit_callback(
|
||||
Event::RedrawRequested(crate::window::WindowId(super::WindowId::X(wid))),
|
||||
Event::RedrawRequested(window_id),
|
||||
&self.target,
|
||||
&mut control_flow,
|
||||
&mut callback,
|
||||
@@ -408,7 +420,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
super::WindowId::X(wid),
|
||||
)) = event
|
||||
{
|
||||
wt.pending_redraws.lock().unwrap().insert(wid);
|
||||
wt.redraw_sender.send(wid).unwrap();
|
||||
} else {
|
||||
callback(event, window_target, control_flow);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use raw_window_handle::unix::XlibHandle;
|
||||
use std::{
|
||||
cmp,
|
||||
collections::HashSet,
|
||||
env,
|
||||
cmp, env,
|
||||
ffi::CString,
|
||||
mem::{self, replace, MaybeUninit},
|
||||
os::raw::*,
|
||||
@@ -12,6 +10,7 @@ use std::{
|
||||
};
|
||||
|
||||
use libc;
|
||||
use mio_extras::channel::Sender;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::{
|
||||
@@ -23,7 +22,7 @@ use crate::{
|
||||
MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes,
|
||||
VideoMode as PlatformVideoMode,
|
||||
},
|
||||
window::{CursorIcon, Fullscreen, Icon, WindowAttributes},
|
||||
window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
|
||||
};
|
||||
|
||||
use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError};
|
||||
@@ -104,7 +103,7 @@ pub struct UnownedWindow {
|
||||
cursor_visible: Mutex<bool>,
|
||||
ime_sender: Mutex<ImeSender>,
|
||||
pub shared_state: Mutex<SharedState>,
|
||||
pending_redraws: Arc<::std::sync::Mutex<HashSet<WindowId>>>,
|
||||
redraw_sender: Sender<WindowId>,
|
||||
}
|
||||
|
||||
impl UnownedWindow {
|
||||
@@ -249,7 +248,7 @@ impl UnownedWindow {
|
||||
cursor_visible: Mutex::new(true),
|
||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||
shared_state: SharedState::new(guessed_monitor, window_attrs.visible),
|
||||
pending_redraws: event_loop.pending_redraws.clone(),
|
||||
redraw_sender: event_loop.redraw_sender.clone(),
|
||||
};
|
||||
|
||||
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
|
||||
@@ -524,23 +523,6 @@ impl UnownedWindow {
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_urgent(&self, is_urgent: bool) {
|
||||
let mut wm_hints = self
|
||||
.xconn
|
||||
.get_wm_hints(self.xwindow)
|
||||
.expect("`XGetWMHints` failed");
|
||||
if is_urgent {
|
||||
(*wm_hints).flags |= ffi::XUrgencyHint;
|
||||
} else {
|
||||
(*wm_hints).flags &= !ffi::XUrgencyHint;
|
||||
}
|
||||
self.xconn
|
||||
.set_wm_hints(self.xwindow, wm_hints)
|
||||
.flush()
|
||||
.expect("Failed to set urgency hint");
|
||||
}
|
||||
|
||||
fn set_netwm(
|
||||
&self,
|
||||
operation: util::StateOperation,
|
||||
@@ -1307,6 +1289,23 @@ impl UnownedWindow {
|
||||
self.set_ime_position_physical(x, y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let mut wm_hints = self
|
||||
.xconn
|
||||
.get_wm_hints(self.xwindow)
|
||||
.expect("`XGetWMHints` failed");
|
||||
if request_type.is_some() {
|
||||
(*wm_hints).flags |= ffi::XUrgencyHint;
|
||||
} else {
|
||||
(*wm_hints).flags &= !ffi::XUrgencyHint;
|
||||
}
|
||||
self.xconn
|
||||
.set_wm_hints(self.xwindow, wm_hints)
|
||||
.flush()
|
||||
.expect("Failed to set urgency hint");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(self.xwindow)
|
||||
@@ -1314,10 +1313,7 @@ impl UnownedWindow {
|
||||
|
||||
#[inline]
|
||||
pub fn request_redraw(&self) {
|
||||
self.pending_redraws
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(WindowId(self.xwindow));
|
||||
self.redraw_sender.send(WindowId(self.xwindow)).unwrap();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -10,6 +10,7 @@ use cocoa::{
|
||||
};
|
||||
use dispatch::Queue;
|
||||
use objc::rc::autoreleasepool;
|
||||
use objc::runtime::NO;
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
@@ -167,7 +168,7 @@ pub unsafe fn set_maximized_async(
|
||||
} else {
|
||||
shared_state_lock.saved_standard_frame()
|
||||
};
|
||||
ns_window.setFrame_display_(new_rect, 0);
|
||||
ns_window.setFrame_display_(new_rect, NO);
|
||||
}
|
||||
|
||||
trace!("Unlocked shared state in `set_maximized`");
|
||||
|
||||
@@ -388,7 +388,7 @@ extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
|
||||
trace!("Triggered `hasMarkedText`");
|
||||
let marked_text: id = *this.get_ivar("markedText");
|
||||
trace!("Completed `hasMarkedText`");
|
||||
(marked_text.length() > 0) as i8
|
||||
(marked_text.length() > 0) as BOOL
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use crate::{
|
||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
icon::Icon,
|
||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||
platform::macos::{ActivationPolicy, RequestUserAttentionType, WindowExtMacOS},
|
||||
platform::macos::{ActivationPolicy, WindowExtMacOS},
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
app_state::INTERRUPT_EVENT_LOOP_EXIT,
|
||||
@@ -28,7 +28,9 @@ use crate::{
|
||||
window_delegate::new_delegate,
|
||||
OsError,
|
||||
},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId},
|
||||
window::{
|
||||
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
|
||||
},
|
||||
};
|
||||
use cocoa::{
|
||||
appkit::{
|
||||
@@ -653,7 +655,7 @@ impl UnownedWindow {
|
||||
self.set_style_mask_async(curr_mask);
|
||||
}
|
||||
|
||||
is_zoomed != 0
|
||||
is_zoomed != NO
|
||||
}
|
||||
|
||||
fn saved_style(&self, shared_state: &mut SharedState) -> NSWindowStyleMask {
|
||||
@@ -977,6 +979,19 @@ impl UnownedWindow {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let ns_request_type = request_type.map(|ty| match ty {
|
||||
UserAttentionType::Critical => NSRequestUserAttentionType::NSCriticalRequest,
|
||||
UserAttentionType::Informational => NSRequestUserAttentionType::NSInformationalRequest,
|
||||
});
|
||||
unsafe {
|
||||
if let Some(ty) = ns_request_type {
|
||||
NSApp().requestUserAttention_(ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// Allow directly accessing the current monitor internally without unwrapping.
|
||||
pub(crate) fn current_monitor_inner(&self) -> RootMonitorHandle {
|
||||
@@ -1030,18 +1045,6 @@ impl WindowExtMacOS for UnownedWindow {
|
||||
*self.ns_view as *mut _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn request_user_attention(&self, request_type: RequestUserAttentionType) {
|
||||
unsafe {
|
||||
NSApp().requestUserAttention_(match request_type {
|
||||
RequestUserAttentionType::Critical => NSRequestUserAttentionType::NSCriticalRequest,
|
||||
RequestUserAttentionType::Informational => {
|
||||
NSRequestUserAttentionType::NSInformationalRequest
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simple_fullscreen(&self) -> bool {
|
||||
let shared_state_lock = self.shared_state.lock().unwrap();
|
||||
@@ -1168,14 +1171,14 @@ unsafe fn set_min_inner_size<V: NSWindow + Copy>(window: V, mut min_size: Logica
|
||||
// If necessary, resize the window to match constraint
|
||||
if current_rect.size.width < min_size.width {
|
||||
current_rect.size.width = min_size.width;
|
||||
window.setFrame_display_(current_rect, 0)
|
||||
window.setFrame_display_(current_rect, NO)
|
||||
}
|
||||
if current_rect.size.height < min_size.height {
|
||||
// The origin point of a rectangle is at its bottom left in Cocoa.
|
||||
// To ensure the window's top-left point remains the same:
|
||||
current_rect.origin.y += current_rect.size.height - min_size.height;
|
||||
current_rect.size.height = min_size.height;
|
||||
window.setFrame_display_(current_rect, 0)
|
||||
window.setFrame_display_(current_rect, NO)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1192,13 +1195,13 @@ unsafe fn set_max_inner_size<V: NSWindow + Copy>(window: V, mut max_size: Logica
|
||||
// If necessary, resize the window to match constraint
|
||||
if current_rect.size.width > max_size.width {
|
||||
current_rect.size.width = max_size.width;
|
||||
window.setFrame_display_(current_rect, 0)
|
||||
window.setFrame_display_(current_rect, NO)
|
||||
}
|
||||
if current_rect.size.height > max_size.height {
|
||||
// The origin point of a rectangle is at its bottom left in Cocoa.
|
||||
// To ensure the window's top-left point remains the same:
|
||||
current_rect.origin.y += current_rect.size.height - max_size.height;
|
||||
current_rect.size.height = max_size.height;
|
||||
window.setFrame_display_(current_rect, 0)
|
||||
window.setFrame_display_(current_rect, NO)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
// incoming events (from the registered handlers) and ensuring they are passed to the user in a
|
||||
// compliant way.
|
||||
|
||||
// Silence warnings from use of deprecated stdweb backend
|
||||
#![allow(deprecated)]
|
||||
|
||||
mod device;
|
||||
mod error;
|
||||
mod event_loop;
|
||||
|
||||
@@ -10,14 +10,12 @@ use stdweb::js;
|
||||
use stdweb::traits::IPointerEvent;
|
||||
use stdweb::unstable::TryInto;
|
||||
use stdweb::web::event::{
|
||||
BlurEvent, ConcreteEvent, FocusEvent, FullscreenChangeEvent, IEvent, KeyDownEvent,
|
||||
KeyPressEvent, KeyUpEvent, MouseWheelEvent, PointerDownEvent, PointerMoveEvent,
|
||||
PointerOutEvent, PointerOverEvent, PointerUpEvent,
|
||||
BlurEvent, ConcreteEvent, FocusEvent, FullscreenChangeEvent, IEvent, IKeyboardEvent,
|
||||
KeyDownEvent, KeyPressEvent, KeyUpEvent, ModifierKey, MouseWheelEvent, PointerDownEvent,
|
||||
PointerMoveEvent, PointerOutEvent, PointerOverEvent, PointerUpEvent,
|
||||
};
|
||||
use stdweb::web::html_element::CanvasElement;
|
||||
use stdweb::web::{
|
||||
document, EventListenerHandle, IChildNode, IElement, IEventTarget, IHtmlElement,
|
||||
};
|
||||
use stdweb::web::{document, EventListenerHandle, IElement, IEventTarget, IHtmlElement};
|
||||
|
||||
pub struct Canvas {
|
||||
/// Note: resizing the CanvasElement should go through `backend::set_canvas_size` to ensure the DPI factor is maintained.
|
||||
@@ -138,7 +136,17 @@ impl Canvas {
|
||||
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||
{
|
||||
self.on_keyboard_press = Some(self.add_user_event(move |event: KeyDownEvent| {
|
||||
event.prevent_default();
|
||||
// event.prevent_default() would suppress subsequent on_received_character() calls. That
|
||||
// supression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to
|
||||
// scroll, etc. We should not do it for key sequences that result in meaningful character
|
||||
// input though.
|
||||
let event_key = &event.key();
|
||||
let is_key_string = event_key.len() == 1 || !event_key.is_ascii();
|
||||
let is_shortcut_modifiers = (event.ctrl_key() || event.alt_key())
|
||||
&& !event.get_modifier_state(ModifierKey::AltGr);
|
||||
if !is_key_string || is_shortcut_modifiers {
|
||||
event.prevent_default();
|
||||
}
|
||||
handler(
|
||||
event::scan_code(&event),
|
||||
event::virtual_key_code(&event),
|
||||
@@ -157,6 +165,8 @@ impl Canvas {
|
||||
// viable/compatible alternative as of now. `beforeinput` is still widely
|
||||
// unsupported.
|
||||
self.on_received_character = Some(self.add_user_event(move |event: KeyPressEvent| {
|
||||
// Supress further handling to stop keys like the space key from scrolling the page.
|
||||
event.prevent_default();
|
||||
handler(event::codepoint(&event));
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -63,8 +63,6 @@ pub fn scale_factor() -> f64 {
|
||||
}
|
||||
|
||||
pub fn set_canvas_size(raw: &CanvasElement, size: Size) {
|
||||
use stdweb::*;
|
||||
|
||||
let scale_factor = scale_factor();
|
||||
|
||||
let physical_size = size.to_physical::<u32>(scale_factor);
|
||||
|
||||
@@ -157,7 +157,17 @@ impl Canvas {
|
||||
self.on_keyboard_press = Some(self.common.add_user_event(
|
||||
"keydown",
|
||||
move |event: KeyboardEvent| {
|
||||
event.prevent_default();
|
||||
// event.prevent_default() would suppress subsequent on_received_character() calls. That
|
||||
// supression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to
|
||||
// scroll, etc. We should not do it for key sequences that result in meaningful character
|
||||
// input though.
|
||||
let event_key = &event.key();
|
||||
let is_key_string = event_key.len() == 1 || !event_key.is_ascii();
|
||||
let is_shortcut_modifiers =
|
||||
(event.ctrl_key() || event.alt_key()) && !event.get_modifier_state("AltGr");
|
||||
if !is_key_string || is_shortcut_modifiers {
|
||||
event.prevent_default();
|
||||
}
|
||||
handler(
|
||||
event::scan_code(&event),
|
||||
event::virtual_key_code(&event),
|
||||
@@ -179,6 +189,8 @@ impl Canvas {
|
||||
self.on_received_character = Some(self.common.add_user_event(
|
||||
"keypress",
|
||||
move |event: KeyboardEvent| {
|
||||
// Supress further handling to stop keys like the space key from scrolling the page.
|
||||
event.prevent_default();
|
||||
handler(event::codepoint(&event));
|
||||
},
|
||||
));
|
||||
|
||||
@@ -59,9 +59,9 @@ impl ScaleChangeDetectorInternal {
|
||||
// We add 0.0001 to the lower and upper bounds such that it won't fail
|
||||
// due to floating point precision limitations.
|
||||
let media_query = format!(
|
||||
"(min-resolution: {:.4}dppx) and (max-resolution: {:.4}dppx)",
|
||||
current_scale - 0.0001,
|
||||
current_scale + 0.0001,
|
||||
"(min-resolution: {min_scale:.4}dppx) and (max-resolution: {max_scale:.4}dppx),
|
||||
(-webkit-min-device-pixel-ratio: {min_scale:.4}) and (-webkit-max-device-pixel-ratio: {max_scale:.4})",
|
||||
min_scale = current_scale - 0.0001, max_scale= current_scale + 0.0001,
|
||||
);
|
||||
let mql = MediaQueryListHandle::new(&media_query, closure);
|
||||
if let Some(mql) = &mql {
|
||||
|
||||
@@ -3,7 +3,9 @@ use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
|
||||
use crate::event;
|
||||
use crate::icon::Icon;
|
||||
use crate::monitor::MonitorHandle as RootMH;
|
||||
use crate::window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWI};
|
||||
use crate::window::{
|
||||
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI,
|
||||
};
|
||||
|
||||
use raw_window_handle::web::WebHandle;
|
||||
|
||||
@@ -268,6 +270,11 @@ impl Window {
|
||||
// Currently a no-op as it does not seem there is good support for this on web
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
|
||||
// Currently an intentional no-op
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// Allow directly accessing the current monitor internally without unwrapping.
|
||||
fn current_monitor_inner(&self) -> RootMH {
|
||||
|
||||
@@ -14,6 +14,8 @@ use winapi::{
|
||||
um::{libloaderapi, uxtheme, winuser},
|
||||
};
|
||||
|
||||
use crate::window::Theme;
|
||||
|
||||
lazy_static! {
|
||||
static ref WIN10_BUILD_VERSION: Option<DWORD> = {
|
||||
// FIXME: RtlGetVersion is a documented windows API,
|
||||
@@ -70,11 +72,14 @@ lazy_static! {
|
||||
static ref LIGHT_THEME_NAME: Vec<u16> = widestring("");
|
||||
}
|
||||
|
||||
/// Attempt to set dark mode on a window, if necessary.
|
||||
/// Returns true if dark mode was set, false if not.
|
||||
pub fn try_dark_mode(hwnd: HWND) -> bool {
|
||||
/// Attempt to set a theme on a window, if necessary.
|
||||
/// Returns the theme that was picked
|
||||
pub fn try_theme(hwnd: HWND, preferred_theme: Option<Theme>) -> Theme {
|
||||
if *DARK_MODE_SUPPORTED {
|
||||
let is_dark_mode = should_use_dark_mode();
|
||||
let is_dark_mode = match preferred_theme {
|
||||
Some(theme) => theme == Theme::Dark,
|
||||
None => should_use_dark_mode(),
|
||||
};
|
||||
|
||||
let theme_name = if is_dark_mode {
|
||||
DARK_THEME_NAME.as_ptr()
|
||||
@@ -84,10 +89,12 @@ pub fn try_dark_mode(hwnd: HWND) -> bool {
|
||||
|
||||
let status = unsafe { uxtheme::SetWindowTheme(hwnd, theme_name as _, std::ptr::null()) };
|
||||
|
||||
status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode)
|
||||
} else {
|
||||
false
|
||||
if status == S_OK && set_dark_mode_for_window(hwnd, is_dark_mode) {
|
||||
return Theme::Dark;
|
||||
}
|
||||
}
|
||||
|
||||
Theme::Light
|
||||
}
|
||||
|
||||
fn set_dark_mode_for_window(hwnd: HWND, is_dark_mode: bool) -> bool {
|
||||
|
||||
@@ -343,6 +343,7 @@ pub fn handle_extended_keys(
|
||||
extended: bool,
|
||||
) -> Option<(c_int, UINT)> {
|
||||
// Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
|
||||
scancode = if extended { 0xE000 } else { 0x0000 } | scancode;
|
||||
let vkey = match vkey {
|
||||
winuser::VK_SHIFT => unsafe {
|
||||
winuser::MapVirtualKeyA(scancode, winuser::MAPVK_VSC_TO_VK_EX) as _
|
||||
@@ -363,20 +364,23 @@ pub fn handle_extended_keys(
|
||||
}
|
||||
_ => {
|
||||
match scancode {
|
||||
// This is only triggered when using raw input. Without this check, we get two events whenever VK_PAUSE is
|
||||
// pressed, the first one having scancode 0x1D but vkey VK_PAUSE...
|
||||
0x1D if vkey == winuser::VK_PAUSE => return None,
|
||||
// ...and the second having scancode 0x45 but an unmatched vkey!
|
||||
0x45 => winuser::VK_PAUSE,
|
||||
// VK_PAUSE and VK_SCROLL have the same scancode when using modifiers, alongside incorrect vkey values.
|
||||
0x46 => {
|
||||
if extended {
|
||||
scancode = 0x45;
|
||||
winuser::VK_PAUSE
|
||||
} else {
|
||||
winuser::VK_SCROLL
|
||||
}
|
||||
// When VK_PAUSE is pressed it emits a LeftControl + NumLock scancode event sequence, but reports VK_PAUSE
|
||||
// as the virtual key on both events, or VK_PAUSE on the first event or 0xFF when using raw input.
|
||||
// Don't emit anything for the LeftControl event in the pair...
|
||||
0xE01D if vkey == winuser::VK_PAUSE => return None,
|
||||
// ...and emit the Pause event for the second event in the pair.
|
||||
0x45 if vkey == winuser::VK_PAUSE || vkey == 0xFF as _ => {
|
||||
scancode = 0xE059;
|
||||
winuser::VK_PAUSE
|
||||
}
|
||||
// VK_PAUSE has an incorrect vkey value when used with modifiers. VK_PAUSE also reports a different
|
||||
// scancode when used with modifiers than when used without
|
||||
0xE046 => {
|
||||
scancode = 0xE059;
|
||||
winuser::VK_PAUSE
|
||||
}
|
||||
// VK_SCROLL has an incorrect vkey value when used with modifiers.
|
||||
0x46 => winuser::VK_SCROLL,
|
||||
_ => vkey,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ use crate::{
|
||||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
|
||||
monitor::MonitorHandle as RootMonitorHandle,
|
||||
platform_impl::platform::{
|
||||
dark_mode::try_dark_mode,
|
||||
dark_mode::try_theme,
|
||||
dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling},
|
||||
drop_handler::FileDropHandler,
|
||||
event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey},
|
||||
@@ -810,7 +810,10 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
event: Destroyed,
|
||||
});
|
||||
subclass_input.event_loop_runner.remove_window(window);
|
||||
0
|
||||
}
|
||||
|
||||
winuser::WM_NCDESTROY => {
|
||||
drop(subclass_input);
|
||||
Box::from_raw(subclass_input_ptr as *mut SubclassInput<T>);
|
||||
0
|
||||
@@ -1304,7 +1307,7 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
event: MouseInput {
|
||||
device_id: DEVICE_ID,
|
||||
state: Pressed,
|
||||
button: Other(xbutton as u8),
|
||||
button: Other(xbutton),
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
});
|
||||
@@ -1326,7 +1329,7 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
event: MouseInput {
|
||||
device_id: DEVICE_ID,
|
||||
state: Released,
|
||||
button: Other(xbutton as u8),
|
||||
button: Other(xbutton),
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
});
|
||||
@@ -1871,20 +1874,20 @@ unsafe extern "system" fn public_window_callback<T: 'static>(
|
||||
winuser::WM_SETTINGCHANGE => {
|
||||
use crate::event::WindowEvent::ThemeChanged;
|
||||
|
||||
let is_dark_mode = try_dark_mode(window);
|
||||
let mut window_state = subclass_input.window_state.lock();
|
||||
let changed = window_state.is_dark_mode != is_dark_mode;
|
||||
let preferred_theme = subclass_input.window_state.lock().preferred_theme;
|
||||
|
||||
if changed {
|
||||
use crate::window::Theme::*;
|
||||
let theme = if is_dark_mode { Dark } else { Light };
|
||||
if preferred_theme == None {
|
||||
let new_theme = try_theme(window, preferred_theme);
|
||||
let mut window_state = subclass_input.window_state.lock();
|
||||
|
||||
window_state.is_dark_mode = is_dark_mode;
|
||||
mem::drop(window_state);
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ThemeChanged(theme),
|
||||
});
|
||||
if window_state.current_theme != new_theme {
|
||||
window_state.current_theme = new_theme;
|
||||
mem::drop(window_state);
|
||||
subclass_input.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId(window)),
|
||||
event: ThemeChanged(new_theme),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||
@@ -1936,7 +1939,7 @@ unsafe extern "system" fn thread_event_target_callback<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 {
|
||||
winuser::WM_DESTROY => {
|
||||
winuser::WM_NCDESTROY => {
|
||||
Box::from_raw(subclass_input);
|
||||
drop(subclass_input);
|
||||
0
|
||||
@@ -1970,7 +1973,8 @@ unsafe extern "system" fn thread_event_target_callback<T: 'static>(
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
// Default WM_PAINT behaviour. This makes sure modals and popups are shown immediatly when opening them.
|
||||
commctrl::DefSubclassProc(window, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
winuser::WM_INPUT_DEVICE_CHANGE => {
|
||||
|
||||
@@ -13,6 +13,7 @@ pub use self::icon::WinIcon as PlatformIcon;
|
||||
|
||||
use crate::event::DeviceId as RootDeviceId;
|
||||
use crate::icon::Icon;
|
||||
use crate::window::Theme;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
@@ -20,6 +21,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub taskbar_icon: Option<Icon>,
|
||||
pub no_redirection_bitmap: bool,
|
||||
pub drag_and_drop: bool,
|
||||
pub preferred_theme: Option<Theme>,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||
@@ -29,6 +31,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||
taskbar_icon: None,
|
||||
no_redirection_bitmap: false,
|
||||
drag_and_drop: true,
|
||||
preferred_theme: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,13 @@ use winapi::{
|
||||
windef::{HWND, POINT, RECT},
|
||||
},
|
||||
um::{
|
||||
combaseapi, dwmapi, libloaderapi,
|
||||
combaseapi, dwmapi,
|
||||
imm::{CFS_POINT, COMPOSITIONFORM},
|
||||
libloaderapi,
|
||||
objbase::COINIT_APARTMENTTHREADED,
|
||||
ole2,
|
||||
oleidl::LPDROPTARGET,
|
||||
shobjidl_core::{CLSID_TaskbarList, ITaskbarList2},
|
||||
wingdi::{CreateRectRgn, DeleteObject},
|
||||
winnt::LPCWSTR,
|
||||
winuser,
|
||||
},
|
||||
@@ -35,7 +36,7 @@ use crate::{
|
||||
icon::Icon,
|
||||
monitor::MonitorHandle as RootMonitorHandle,
|
||||
platform_impl::platform::{
|
||||
dark_mode::try_dark_mode,
|
||||
dark_mode::try_theme,
|
||||
dpi::{dpi_to_scale_factor, hwnd_dpi},
|
||||
drop_handler::FileDropHandler,
|
||||
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
||||
@@ -44,7 +45,7 @@ use crate::{
|
||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||
PlatformSpecificWindowBuilderAttributes, WindowId,
|
||||
},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
||||
window::{CursorIcon, Fullscreen, Theme, UserAttentionType, WindowAttributes},
|
||||
};
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
@@ -490,7 +491,14 @@ impl Window {
|
||||
|
||||
// Update window style
|
||||
WindowState::set_window_flags(window_state_lock, window.0, |f| {
|
||||
f.set(WindowFlags::MARKER_FULLSCREEN, fullscreen.is_some())
|
||||
f.set(
|
||||
WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN,
|
||||
matches!(fullscreen, Some(Fullscreen::Exclusive(_))),
|
||||
);
|
||||
f.set(
|
||||
WindowFlags::MARKER_BORDERLESS_FULLSCREEN,
|
||||
matches!(fullscreen, Some(Fullscreen::Borderless(_))),
|
||||
);
|
||||
});
|
||||
|
||||
// Update window bounds
|
||||
@@ -610,14 +618,61 @@ impl Window {
|
||||
self.window_state.lock().taskbar_icon = taskbar_icon;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_position(&self, _position: Position) {
|
||||
warn!("`Window::set_ime_position` is ignored on Windows")
|
||||
pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) {
|
||||
if unsafe { winuser::GetSystemMetrics(winuser::SM_IMMENABLED) } != 0 {
|
||||
let mut composition_form = COMPOSITIONFORM {
|
||||
dwStyle: CFS_POINT,
|
||||
ptCurrentPos: POINT { x, y },
|
||||
rcArea: unsafe { mem::zeroed() },
|
||||
};
|
||||
unsafe {
|
||||
let himc = winapi::um::imm::ImmGetContext(self.window.0);
|
||||
winapi::um::imm::ImmSetCompositionWindow(himc, &mut composition_form);
|
||||
winapi::um::imm::ImmReleaseContext(self.window.0, himc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_dark_mode(&self) -> bool {
|
||||
self.window_state.lock().is_dark_mode
|
||||
pub fn set_ime_position(&self, spot: Position) {
|
||||
let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
|
||||
self.set_ime_position_physical(x, y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
let window = self.window.clone();
|
||||
let active_window_handle = unsafe { winuser::GetActiveWindow() };
|
||||
if window.0 == active_window_handle {
|
||||
return;
|
||||
}
|
||||
|
||||
self.thread_executor.execute_in_thread(move || unsafe {
|
||||
let (flags, count) = request_type
|
||||
.map(|ty| match ty {
|
||||
UserAttentionType::Critical => {
|
||||
(winuser::FLASHW_ALL | winuser::FLASHW_TIMERNOFG, u32::MAX)
|
||||
}
|
||||
UserAttentionType::Informational => {
|
||||
(winuser::FLASHW_TRAY | winuser::FLASHW_TIMERNOFG, 0)
|
||||
}
|
||||
})
|
||||
.unwrap_or((winuser::FLASHW_STOP, 0));
|
||||
|
||||
let mut flash_info = winuser::FLASHWINFO {
|
||||
cbSize: mem::size_of::<winuser::FLASHWINFO>() as UINT,
|
||||
hwnd: window.0,
|
||||
dwFlags: flags,
|
||||
uCount: count,
|
||||
dwTimeout: 0,
|
||||
};
|
||||
winuser::FlashWindowEx(&mut flash_info);
|
||||
});
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn theme(&self) -> Theme {
|
||||
self.window_state.lock().current_theme
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,47 +763,34 @@ unsafe fn init<T: 'static>(
|
||||
|
||||
// making the window transparent
|
||||
if attributes.transparent && !pl_attribs.no_redirection_bitmap {
|
||||
let region = CreateRectRgn(0, 0, -1, -1); // makes the window transparent
|
||||
|
||||
let bb = dwmapi::DWM_BLURBEHIND {
|
||||
dwFlags: dwmapi::DWM_BB_ENABLE | dwmapi::DWM_BB_BLURREGION,
|
||||
dwFlags: dwmapi::DWM_BB_ENABLE,
|
||||
fEnable: 1,
|
||||
hRgnBlur: region,
|
||||
hRgnBlur: ptr::null_mut(),
|
||||
fTransitionOnMaximized: 0,
|
||||
};
|
||||
|
||||
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
||||
DeleteObject(region as _);
|
||||
|
||||
if attributes.decorations {
|
||||
// HACK: When opaque (opacity 255), there is a trail whenever
|
||||
// the transparent window is moved. By reducing it to 254,
|
||||
// the window is rendered properly.
|
||||
let opacity = 254;
|
||||
let opacity = 255;
|
||||
|
||||
// The color key can be any value except for black (0x0).
|
||||
let color_key = 0x0030c100;
|
||||
|
||||
winuser::SetLayeredWindowAttributes(
|
||||
real_window.0,
|
||||
color_key,
|
||||
opacity,
|
||||
winuser::LWA_ALPHA,
|
||||
);
|
||||
winuser::SetLayeredWindowAttributes(real_window.0, 0, opacity, winuser::LWA_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
// If the system theme is dark, we need to set the window theme now
|
||||
// before we update the window flags (and possibly show the
|
||||
// window for the first time).
|
||||
let dark_mode = try_dark_mode(real_window.0);
|
||||
let current_theme = try_theme(real_window.0, pl_attribs.preferred_theme);
|
||||
|
||||
let window_state = {
|
||||
let window_state = WindowState::new(
|
||||
&attributes,
|
||||
pl_attribs.taskbar_icon,
|
||||
scale_factor,
|
||||
dark_mode,
|
||||
current_theme,
|
||||
pl_attribs.preferred_theme,
|
||||
);
|
||||
let window_state = Arc::new(Mutex::new(window_state));
|
||||
WindowState::set_window_flags(window_state.lock(), real_window.0, |f| *f = window_flags);
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
event::ModifiersState,
|
||||
icon::Icon,
|
||||
platform_impl::platform::{event_loop, util},
|
||||
window::{CursorIcon, Fullscreen, WindowAttributes},
|
||||
window::{CursorIcon, Fullscreen, Theme, WindowAttributes},
|
||||
};
|
||||
use parking_lot::MutexGuard;
|
||||
use std::{io, ptr};
|
||||
@@ -31,7 +31,8 @@ pub struct WindowState {
|
||||
|
||||
pub modifiers_state: ModifiersState,
|
||||
pub fullscreen: Option<Fullscreen>,
|
||||
pub is_dark_mode: bool,
|
||||
pub current_theme: Theme,
|
||||
pub preferred_theme: Option<Theme>,
|
||||
pub high_surrogate: Option<u16>,
|
||||
window_flags: WindowFlags,
|
||||
}
|
||||
@@ -71,7 +72,8 @@ bitflags! {
|
||||
|
||||
/// Marker flag for fullscreen. Should always match `WindowState::fullscreen`, but is
|
||||
/// included here to make masking easier.
|
||||
const MARKER_FULLSCREEN = 1 << 9;
|
||||
const MARKER_EXCLUSIVE_FULLSCREEN = 1 << 9;
|
||||
const MARKER_BORDERLESS_FULLSCREEN = 1 << 13;
|
||||
|
||||
/// 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
|
||||
@@ -89,7 +91,7 @@ bitflags! {
|
||||
WindowFlags::RESIZABLE.bits |
|
||||
WindowFlags::MAXIMIZED.bits
|
||||
);
|
||||
const 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;
|
||||
const INVISIBLE_AND_MASK = !WindowFlags::MAXIMIZED.bits;
|
||||
}
|
||||
@@ -100,7 +102,8 @@ impl WindowState {
|
||||
attributes: &WindowAttributes,
|
||||
taskbar_icon: Option<Icon>,
|
||||
scale_factor: f64,
|
||||
is_dark_mode: bool,
|
||||
current_theme: Theme,
|
||||
preferred_theme: Option<Theme>,
|
||||
) -> WindowState {
|
||||
WindowState {
|
||||
mouse: MouseProperties {
|
||||
@@ -121,7 +124,8 @@ impl WindowState {
|
||||
|
||||
modifiers_state: ModifiersState::default(),
|
||||
fullscreen: None,
|
||||
is_dark_mode,
|
||||
current_theme,
|
||||
preferred_theme,
|
||||
high_surrogate: None,
|
||||
window_flags: WindowFlags::empty(),
|
||||
}
|
||||
@@ -176,9 +180,11 @@ impl MouseProperties {
|
||||
|
||||
impl WindowFlags {
|
||||
fn mask(mut self) -> WindowFlags {
|
||||
if self.contains(WindowFlags::MARKER_FULLSCREEN) {
|
||||
if self.contains(WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN) {
|
||||
self &= WindowFlags::FULLSCREEN_AND_MASK;
|
||||
self |= WindowFlags::EXCLUSIVE_FULLSCREEN_OR_MASK;
|
||||
} else if self.contains(WindowFlags::MARKER_BORDERLESS_FULLSCREEN) {
|
||||
self &= WindowFlags::FULLSCREEN_AND_MASK;
|
||||
self |= WindowFlags::FULLSCREEN_OR_MASK;
|
||||
}
|
||||
if !self.contains(WindowFlags::VISIBLE) {
|
||||
self &= WindowFlags::INVISIBLE_AND_MASK;
|
||||
@@ -319,7 +325,9 @@ impl WindowFlags {
|
||||
// We generally don't want style changes here to affect window
|
||||
// focus, but for fullscreen windows they must be activated
|
||||
// (i.e. focused) so that they appear on top of the taskbar
|
||||
if !new.contains(WindowFlags::MARKER_FULLSCREEN) {
|
||||
if !new.contains(WindowFlags::MARKER_EXCLUSIVE_FULLSCREEN)
|
||||
&& !new.contains(WindowFlags::MARKER_BORDERLESS_FULLSCREEN)
|
||||
{
|
||||
flags |= winuser::SWP_NOACTIVATE;
|
||||
}
|
||||
|
||||
|
||||
@@ -677,11 +677,28 @@ impl Window {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web / Windows:** Unsupported.
|
||||
/// - **iOS / Android / Web:** Unsupported.
|
||||
#[inline]
|
||||
pub fn set_ime_position<P: Into<Position>>(&self, position: P) {
|
||||
self.window.set_ime_position(position.into())
|
||||
}
|
||||
|
||||
/// Requests user attention to the window, this has no effect if the application
|
||||
/// is already focused. How requesting for user attention manifests is platform dependent,
|
||||
/// see `UserAttentionType` for details.
|
||||
///
|
||||
/// Providing `None` will unset the request for user attention. Unsetting the request for
|
||||
/// user attention might not be done automatically by the WM when the window receives input.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS / Android / Web / Wayland:** Unsupported.
|
||||
/// - **macOS:** `None` has no effect.
|
||||
/// - **X11:** Requests for user attention must be manually cleared.
|
||||
#[inline]
|
||||
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
|
||||
self.window.request_user_attention(request_type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Cursor functions.
|
||||
@@ -784,6 +801,12 @@ impl Window {
|
||||
}
|
||||
|
||||
unsafe impl raw_window_handle::HasRawWindowHandle for Window {
|
||||
/// Returns a `raw_window_handle::RawWindowHandle` for the Window
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android:** Only available after receiving the Resumed event and before Suspended. *If you*
|
||||
/// *try to get the handle outside of that period, this function will panic*!
|
||||
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
|
||||
self.window.raw_window_handle()
|
||||
}
|
||||
@@ -863,8 +886,29 @@ pub enum Fullscreen {
|
||||
Borderless(Option<MonitorHandle>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Theme {
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between `Critical` and `Informational`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum UserAttentionType {
|
||||
/// ## Platform-specific
|
||||
/// - **macOS:** Bounces the dock icon until the application is in focus.
|
||||
/// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
|
||||
Critical,
|
||||
/// ## Platform-specific
|
||||
/// - **macOS:** Bounces the dock icon once.
|
||||
/// - **Windows:** Flashes the taskbar button until the application is in focus.
|
||||
Informational,
|
||||
}
|
||||
|
||||
impl Default for UserAttentionType {
|
||||
fn default() -> Self {
|
||||
UserAttentionType::Informational
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user