Compare commits

..

29 Commits

Author SHA1 Message Date
Kirill Chibisov
6db308f1e9 Release 0.24.0 2020-12-10 19:12:46 +03:00
Viktor Zoutman
6f70fd90b9 Windows: Changed thread_event_target_callback's WM_DESTROY to WM_NCDESTROY (#1780) 2020-12-10 12:09:08 +01:00
moko256
db038d943c On Windows, implement 'Window::set_ime_position' with IMM API 2020-12-09 23:16:59 +03:00
Kirill Chibisov
c5620efc9c On Wayland, don't drop extra mouse buttons
This commit forwards "unknown" Wayland mouse buttons downstream via
'MouseButton::Other'. Possible values for those could be found in
<linux/input-event-codes.h>.

Also, since Wayland just forwards buttons from the kernel, which are
'u16', we must adjust 'MouseButton::Other' to take 'u16' instead of
'u8'.
2020-12-09 23:11:25 +03:00
Marnix Kuijs
8fb7aa5cef Android: Improved multi-touch (#1783)
* Improved multi-touch

* Update feature matrix

* Generate cancelled events for all pointers

* Changed back features matrix layout

* Reduced code duplication

* Updated changelog

* Revert changelog update
2020-12-02 12:13:42 -08:00
Viktor Zoutman
6ddee9a8ac Ability to force a theme on Windows (#1666) 2020-11-30 19:04:26 +01:00
Max de Danschutter
5700359a61 Android: support multi-touch (#1776) 2020-11-28 17:41:11 +01:00
Max de Danschutter
0861a353d6 Add 'request_user_attention' to Window
This commit introduces a cross platform way to request a user attention
to the window via a 'request_user_attention' method on a Window struct.
This method is inspired by macOS's 'request_user_attention' method and
thus reuses its signature and semantics to some extent.
2020-11-27 05:03:08 +03:00
Philippe Renon
f79efec7ef Fix deprecation warning in the window icon example 2020-11-26 00:20:35 +00:00
Viktor Zoutman
77d5d20391 Windows: Delayed Message Boxes Fix. (#1769) 2020-11-24 23:05:29 +01:00
Kirill Chibisov
165e51d850 On Wayland, increase default font size in CSD
This commit increased default font size from 11 to 17 making it
identical to the one SCTK is using under the hood, since it's more
readable.
2020-11-22 01:53:56 +03:00
Max de Danschutter
1c38f113b3 Remove println call from Android's eventloop 2020-11-19 17:56:24 +00:00
msiglreith
66859607a3 Rename desktop eventloop extensions to run_return extension (#1738) 2020-11-12 20:49:44 +01:00
Wladimir J. van der Laan
edf396b1a4 On Wayland, add missing mappings for numpad arrows
The mappings for 'keysyms::XKB_KEY_KP_{Up,Down,Left,Rigt}' were missing making the arrow keys on the numpad not sending proper 'VirtualKeyCode's.
2020-11-11 00:55:29 +03:00
Murarth
cbeb51b436 X11: Fix multiple RedrawRequested events per event loop iteration (#1758)
* X11: Fix multiple RedrawRequested per event loop iteration

* Prevent infinite loop
2020-11-07 11:46:37 -07:00
Murarth
45e4fd6ec1 X11: Fix request_redraw not waking the event loop (#1756) 2020-11-05 16:42:03 -07:00
Mikko Lehtonen
3a077ff211 macos: Fix compile on aarch64 2020-11-02 21:06:00 +00:00
Brad
be850e483a Document Android raw_window_handle requirements (#1749)
* Add docs describing raw_winow_handle on android

* Run cargo fmt

* Change raw_window_handle panic to give more info
2020-10-29 14:23:46 -07:00
Simon Hausmann
33fb62bb25 Fix WindowEvent::ReceivedCharacter on web (#1747)
* Fix WindowEvent::ReceivedCharacter on web

The event was never sent to the application because of the unconditional
preventDefault() call on keydown.

Fixes #1741

* Don't scroll when pressing space on a focused canvas

After reaching keypress, we should prevent further propagation.

Relates to #1741
2020-10-29 17:13:21 -04:00
qthree
66c117e599 [Windows] Fix use after free during window destruction (#1746) 2020-10-23 19:04:18 +02:00
Waridley
8aa1be8336 On Unix, fix cross-compiling to wasm32
Aborting compilation by using 'compile_error!' macro in build.rs was resulting in failing cross
compilation, thus this commit removes build.rs. The compilation will now be aborted on existing 'compile_error!' macros in corresponding platform sources.
2020-10-22 07:14:33 +03:00
Kirill Chibisov
037d4121a1 On Wayland, fix 'with_min_inner_size' disabling resize
Building window with 'set_min_inner_size' was setting 'max_inner_size'
under the hood, thus completely disabling window resize, since
the window isn't resizeable on Wayland when its minimum size
is equal to its maximum size.
2020-10-20 03:30:19 +03:00
Vickles
fbd3918d3a Add prefix byte for extended scancodes on Windows (#1679) 2020-10-19 16:35:01 +02:00
Alex Butler
7c543a43a9 Windows: Fix alt tab bordless fullscreen (#1740) 2020-10-19 16:15:23 +02:00
Kirill Chibisov
ee3996cac6 Feature gate more dependencies
Wayland backend is self contained and only requires
smithay-client-toolkit to work, thus feature gate the
rest of the deps that are only for X11.
2020-10-18 02:05:08 +03:00
Murarth
96809ac659 Fix warnings (#1742) 2020-10-15 11:33:06 -07:00
Jim Porter
6343059bc0 Fix Windows transparency behavior to support fully-opaque regions (#1621)
This patch removes an unneeded workaround for transparent windows on the
Windows platform. In addition, it simplifies a couple of related API calls:

* Remove the `CreateRectRgn` call, since we want the entire window's region to
  have blur behind it, and `DwnEnableBlurBehindWindow` does that by default.
* Remove the `color_key` for `SetLayeredWindowAttributes`, since it's not used
  (we're not passing `winuser::LWA_COLORKEY` to the flags).
2020-10-14 12:23:34 +02:00
Simon Hausmann
5a78fe33e8 Fix failing assertion on start-up with Safari (#1736)
The initial media query that's used to watch for device pixel ratio
changes should match. Unfortunately it doesn't with Safari and the
corresponding assertion fails. This is because resolution is not a
supported support property for queries. As a workaround, this patch
extends the query to optionally match for the webkit specific DPR
property directly.

This fixes #1734
2020-10-10 00:31:51 -04:00
Max de Danschutter
676fb947f2 Added WindowHasFocus and WindowLostFocus events to Android (#1733)
* Added WindowHasFocus and WindowLostFocus events to Android

* Update changelog
2020-10-08 19:44:41 +02:00
40 changed files with 553 additions and 305 deletions

View File

@@ -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)

View File

@@ -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"

View File

@@ -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] |❌ |❌ |❌ |❓ |

View File

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

View File

@@ -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() {}

View 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;
}
_ => (),
}
});
}

View File

@@ -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)

View File

@@ -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();

View File

@@ -745,7 +745,7 @@ pub enum MouseButton {
Left,
Right,
Middle,
Other(u8),
Other(u16),
}
/// Describes a difference in the mouse scroll wheel state.

View File

@@ -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]

View File

@@ -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()

View File

@@ -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;

View File

@@ -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)

View File

@@ -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.))
}
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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())

View File

@@ -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),

View File

@@ -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(

View File

@@ -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);

View File

@@ -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,
},
}),

View File

@@ -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);
}

View File

@@ -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]

View File

@@ -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`");

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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;

View File

@@ -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));
}));
}

View File

@@ -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);

View File

@@ -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));
},
));

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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,
}
}

View File

@@ -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 => {

View File

@@ -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,
}
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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
}
}