Compare commits

...

29 Commits

Author SHA1 Message Date
Joe Moon
30aa5a5057 version 0.18.0 (#680)
* version 0.18.0

* Changelog: F16-F24 support was a breaking change

* fix version in README

* bump image dep

* Updated release date
2018-11-07 00:43:15 -05:00
Barret Rennie
a46fcaee31 Support requesting user attention on macOS (#664)
* Support requesting user attention on macOS

* Documentation improvements
2018-11-06 23:50:40 -05:00
Osspial
52e2748869 Remove From<NSApplicationActivationPolicy> impl from ActivationPolicy (#690)
* Remove From<NSApplicationActivationPolicy> impl from ActivationPolicy

* Update CHANGELOG
2018-11-05 18:54:22 -05:00
Patrick Walton
d2d127a4c4 Make views explicitly layer-backed on macOS Mojave. (#685)
On Mojave, views automatically become layer-backed shortly after being added to
a window. Changing the layer-backedness of a view breaks the association
between the view and its associated OpenGL context. To work around this, on
Mojave we explicitly make the view layer-backed up front so that AppKit doesn't
do it itself and break the association with its context.

This was breaking the `window` example in `glutin`.
2018-11-05 14:34:54 -05:00
Francesca Plebani
0fca8e8cb5 X11: Fix DND freezing the WM (#688)
Fixes #687

`XdndFinished` isn't supposed to be sent when rejecting a `XdndPosition`; it should only be
sent in response to `XdndDrop`.

https://freedesktop.org/wiki/Specifications/XDND/
2018-11-02 17:41:51 -04:00
Osspial
6bec912961 Add optional Serde implementations and missing derivable traits (#652)
* Add optional serde feature

* Document features in README

* Add changelog entry

* Implement some missing derivable traits

* Add changelog entry for std derives

* Remove extraneous space on serde doc comments

* Add period to end of serde line in readme

* Remove serde impls from WindowAttributes

* Add serde impls for TouchPhase

* Add serde test file

* Add feature lines to testing CIs

* Remove WindowAttributes from changelog
2018-11-01 04:24:56 -04:00
Artúr Kovács
214e157e5d Implement HoveredFile and HoveredFileCancelled on Windows (#662)
* Implement HoveredFile and HoveredFileCancelled on Windows (#448)

* Update CHANGELOG.

* Applied code organizational corrections and fixed IDropHandler leak on window destroy.

* Moved FileDropHandle to a separate file.
2018-10-24 14:40:12 -04:00
Lucas Kent
da1d479e55 update to image 0.20 (#683) 2018-10-23 20:29:11 -04:00
Eleanore Young
062bb0cef2 On linux without X11 or Wayland, reduced the panic message to a single line (#681) 2018-10-21 18:12:51 -04:00
Victor Berger
c744b016ce x11: compute resize logical size with new dpi (#668)
* x11: compute resize logical size with new dpi

Whenever a dpi change occurs, trigger a Resized event as well with the
new logical size. Given X11 primarily deals in physical pixels, a change
in DPI also changes the logical size (as the physical size remains
fixed).

* Doc tweaks
2018-10-19 16:32:57 -04:00
George Burton
f486845f7f Implement Debug trait on exported opaque types (#677)
* Implement `Debug` trait on exported opaque types

* Make formatting consistent
2018-10-17 23:20:12 -04:00
trimental
7baa96c5c7 Provide current modifiers state with pointer events on wayland (#676) 2018-10-17 22:34:02 -04:00
Joe Moon
ea07ec1fda macOS: fix modifiers during key repeat (#666)
* macOS: fix modifiers during key repeat

* fix compile warnings
2018-10-17 22:03:26 -04:00
Alex Taylor
26b70e457b Windows: Fix transparency (#675)
* Windows: Fix transparency (#260)

* Windows: Only enable WS_EX_LAYERED for transparent windows

* Update winapi to 0.3.6

* Windows: Amend transparency code

* Add transparency fix to CHANGELOG.md
2018-10-17 20:23:59 -04:00
Rob Horswell
5d5fcb3911 Windows: Fix window.set_maximized() (#672)
* Windows: Fix window.set_maximized()

* Add window.set_maximized fix to CHANGELOG.

* Windows: use same style for set_maximized as other set_x(bool) methods
2018-10-14 19:47:08 -04:00
trimental
50008dff3d Upgrade to smithay-client-toolkit 0.4 (#671)
* Upgrade to smithay-client-toolkit 0.4

* Fix PR points
2018-10-14 19:15:43 -04:00
Francesca Plebani
808638fee3 Windows: Fix set_cursor delay (#660) 2018-09-22 21:03:38 -04:00
Tobias Umbach
b0e3865562 Don't include NUL byte in _NET_WM_NAME (#658)
> The contents of the property are not required to be null-terminated;
> any terminating null should not be included in text_prop.nitems.

https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XmbTextPropertyToTextList.html
2018-09-20 17:59:37 -04:00
Tobias Umbach
bc03ffb317 Add X11-specific with_gtk_theme_variant option (#659) 2018-09-20 17:00:04 -04:00
trimental
1edbca1775 Wayland: use init_from_env() to create windows and allow server-sid… (#655)
* Wayland: use `init_from_env()` to create windows and allow server-side decorations

* Change the CHANGELOG.md entrys wording
2018-09-20 13:48:36 -04:00
Kirill Chibisov
5a0bc016e7 Add support for F16-F24 (#641)
* Added support for F16-F19 keys.

* Documented support for F16-F19 keys

* Added support for F20 key

* Added support for F21-F24 on platforms except macOs

* Added support for F21-F24 on macOs

* Documented addition of F16-F24 keys

* Added missing ref qualifier

* Fixed compilation error on 1.24.1

* Refactored methods in macOs events_loop and view files
2018-09-12 13:04:16 -04:00
Sven-Hendrik Haase
bb66b7f28e Update wm spec hints (#646)
* Update wm-spec hints to v1.5

* Update changelog

* Fix CHANGELOG entry

* Remove trailing quote
2018-09-11 15:03:42 -04:00
Sven-Hendrik Haase
0331491b2b Put badges next to each other instead of below eachother (#649) 2018-09-11 14:59:04 -04:00
Kornel
54a782c8ae Syntax fix in Cargo.toml (#644) 2018-09-11 00:23:48 -04:00
Osspial
a70bc20829 Remove resize block on Windows (#634)
* Remove Windows block on resize

* Add CHANGELOG entry

* Move CHANGELOG entry to Unreleased

* Further edits to CHANGELOG entry
2018-08-24 13:48:57 -04:00
trimental
102ed3b800 Wayland: commit frame surface on resize (#635) 2018-08-23 13:20:02 -04:00
Joe Moon
c8e339fe6d version 0.17.2 (#630)
* version 0.17.2

* Update release date
2018-08-19 18:27:57 -04:00
trimental
e4e53fe315 Add key repetition for the wayland backend (#628)
* Add key repetition for the wayland backend

* Upgrade smithay-client-toolkit to 0.3.0
2018-08-19 17:17:40 -04:00
Joe Moon
1c795c3f1c 625 macos modifiers (#629)
* fix <C-Tab>

* fix <CMD-{key}>

* move the NSKeyUp special handling into the match statement

* add special handling for `<Cmd-.>`

* formatting

* add return type to msg_send!
2018-08-15 19:42:57 -04:00
32 changed files with 1179 additions and 595 deletions

View File

@@ -65,8 +65,12 @@ install:
script:
- cargo build --target $TARGET --verbose
- cargo build --target $TARGET --features serde --verbose
- cargo build --target $TARGET --features icon_loading --verbose
# Running iOS apps on OSX requires the simulator so we skip that for now
- if [ "$TARGET" != "x86_64-apple-ios" ]; then cargo test --target $TARGET --verbose; fi
- if [ "$TARGET" != "x86_64-apple-ios" ]; then cargo test --target $TARGET --features serde --verbose; fi
- if [ "$TARGET" != "x86_64-apple-ios" ]; then cargo test --target $TARGET --features icon_loading --verbose; fi
after_success:
- |

View File

@@ -1,5 +1,41 @@
# Unreleased
# Version 0.18.0 (2018-11-07)
- **Breaking:** `image` crate upgraded to 0.20. This is exposed as part of the `icon_loading` API.
- On Wayland, pointer events will now provide the current modifiers state.
- On Wayland, titles will now be displayed in the window header decoration.
- On Wayland, key repetition is now ended when keyboard loses focus.
- On Wayland, windows will now use more stylish and modern client side decorations.
- On Wayland, windows will use server-side decorations when available.
- **Breaking:** Added support for F16-F24 keys (variants were added to the `VirtualKeyCode` enum).
- Fixed graphical glitches when resizing on Wayland.
- On Windows, fix freezes when performing certain actions after a window resize has been triggered. Reintroduces some visual artifacts when resizing.
- Updated window manager hints under X11 to v1.5 of [Extended Window Manager Hints](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html#idm140200472629520).
- Added `WindowBuilderExt::with_gtk_theme_variant` to X11-specific `WindowBuilder` functions.
- Fixed UTF8 handling bug in X11 `set_title` function.
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
- On Windows, the `HoveredFile` and `HoveredFileCancelled` events are now implemented.
- On Windows, fix `Window::set_maximized`.
- On Windows 10, fix transparency (#260).
- On macOS, fix modifiers during key repeat.
- Implemented the `Debug` trait for `Window`, `EventsLoop`, `EventsLoopProxy` and `WindowBuilder`.
- On X11, now a `Resized` event will always be generated after a DPI change to ensure the window's logical size is consistent with the new DPI.
- Added further clarifications to the DPI docs.
- On Linux, if neither X11 nor Wayland manage to initialize, the corresponding panic now consists of a single line only.
- Add optional `serde` feature with implementations of `Serialize`/`Deserialize` for DPI types and various event types.
- Add `PartialEq`, `Eq`, and `Hash` implementations on public types that could have them but were missing them.
- On X11, drag-and-drop receiving an unsupported drop type can no longer cause the WM to freeze.
- Fix issue whereby the OpenGL context would not appear at startup on macOS Mojave (#1069).
- **Breaking:** Removed `From<NSApplicationActivationPolicy>` impl from `ActivationPolicy` on macOS.
- On macOS, the application can request the user's attention with `WindowExt::request_user_attention`.
# Version 0.17.2 (2018-08-19)
- On macOS, fix `<C-Tab>` so applications receive the event.
- On macOS, fix `<Cmd-{key}>` so applications receive the event.
- On Wayland, key press events will now be repeated.
# Version 0.17.1 (2018-08-05)
- On X11, prevent a compilation failure in release mode for versions of Rust greater than or equal to 1.30.

View File

@@ -1,7 +1,7 @@
[package]
name = "winit"
version = "0.17.1"
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
version = "0.18.0"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
keywords = ["windowing"]
license = "Apache-2.0"
@@ -11,7 +11,7 @@ documentation = "https://docs.rs/winit"
categories = ["gui"]
[package.metadata.docs.rs]
features = ["icon_loading"]
features = ["icon_loading", "serde"]
[features]
icon_loading = ["image"]
@@ -20,7 +20,8 @@ icon_loading = ["image"]
lazy_static = "1"
libc = "0.2"
log = "0.4"
image = { version = "0.19", optional = true }
image = { version = "0.20.1", optional = true }
serde = { version = "1", optional = true, features = ["serde_derive"] }
[target.'cfg(target_os = "android")'.dependencies.android_glue]
version = "0.2"
@@ -30,32 +31,36 @@ objc = "0.2.3"
[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2.3"
cocoa = "0.17"
cocoa = "0.18.4"
core-foundation = "0.6"
core-graphics = "0.16"
core-graphics = "0.17.3"
[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3.5"
version = "0.3.6"
features = [
"combaseapi",
"dwmapi",
"errhandlingapi",
"hidusage",
"libloaderapi",
"objbase",
"ole2",
"processthreadsapi",
"shellapi",
"shellscalingapi",
"shobjidl_core",
"unknwnbase",
"winbase",
"windowsx",
"winerror",
"wingdi",
"winnt",
"winuser",
]
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
wayland-client = { version = "0.20.10", features = [ "dlopen", "egl", "cursor"] }
smithay-client-toolkit = "0.2.6"
wayland-client = { version = "0.21", features = [ "dlopen", "egl", "cursor"] }
smithay-client-toolkit = "0.4"
x11-dl = "2.18.3"
parking_lot = "0.6"
percent-encoding = "1.0"

View File

@@ -1,15 +1,13 @@
# winit - Cross-platform window creation and management in Rust
[![](http://meritbadge.herokuapp.com/winit)](https://crates.io/crates/winit)
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/winit)
[![Build Status](https://travis-ci.org/tomaka/winit.svg?branch=master)](https://travis-ci.org/tomaka/winit)
[![Build status](https://ci.appveyor.com/api/projects/status/5h87hj0g4q2xe3j9/branch/master?svg=true)](https://ci.appveyor.com/project/tomaka/winit/branch/master)
```toml
[dependencies]
winit = "0.17"
winit = "0.18"
```
## [Documentation](https://docs.rs/winit)
@@ -43,6 +41,12 @@ fn main() {
}
```
### Cargo Features
Winit provides the following features, which can be enabled in your `Cargo.toml` file:
* `icon_loading`: Enables loading window icons directly from files. Depends on the [`image` crate](https://crates.io/crates/image).
* `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
### Platform-specific usage
#### Emscripten and WebAssembly

View File

@@ -22,3 +22,5 @@ build: false
test_script:
- cargo test --verbose
- cargo test --features serde --verbose
- cargo test --features icon_loading --verbose

View File

@@ -6,10 +6,9 @@ extern crate winit;
#[cfg(feature = "icon_loading")]
extern crate image;
use winit::Icon;
#[cfg(feature = "icon_loading")]
fn main() {
use winit::Icon;
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
// since it seems to work well enough in most cases. Be careful about going too high, or
@@ -37,7 +36,7 @@ fn main() {
match event {
CloseRequested => return winit::ControlFlow::Break,
DroppedFile(path) => {
use image::GenericImage;
use image::GenericImageView;
let icon_image = image::open(path).expect("Failed to open window icon");

View File

@@ -29,9 +29,9 @@
//! them entering an existential panic. Once users enter that state, they will no longer be focused on your application.
//!
//! There are two ways to get the DPI factor:
//! - You can track the `WindowEvent::HiDpiFactorChanged` event of your windows. This event is sent any
//! time the DPI factor changes, be it because the window moved to another monitor, or because the
//! user changed the configuration of their screen.
//! - You can track the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) event of your
//! windows. This event is sent any time the DPI factor changes, either because the window moved to another monitor,
//! or because the user changed the configuration of their screen.
//! - You can also retrieve the DPI factor of a monitor by calling
//! [`MonitorId::get_hidpi_factor`](../struct.MonitorId.html#method.get_hidpi_factor), or the
//! current DPI factor applied to a window by calling
@@ -40,8 +40,8 @@
//!
//! Depending on the platform, the window's actual DPI factor may only be known after
//! the event loop has started and your window has been drawn once. To properly handle these cases,
//! the most robust way is to monitor the `WindowEvent::HiDpiFactorChanged` event and dynamically
//! adapt your drawing logic to follow the DPI factor.
//! the most robust way is to monitor the [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged)
//! event and dynamically adapt your drawing logic to follow the DPI factor.
//!
//! Here's an overview of what sort of DPI factors you can expect, and where they come from:
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the display settings.
@@ -53,18 +53,28 @@
//! - **X11:** On X11, we calcuate the DPI factor based on the millimeter dimensions provided by XRandR. This can
//! result in a wide range of possible values, including some interesting ones like 1.0833333333333333. This can be
//! overridden using the `WINIT_HIDPI_FACTOR` environment variable, though that's not recommended.
//! - **Wayland:** On Wayland, DPI factors are very much at the discretion of the user.
//! - **Wayland:** On Wayland, DPI factors are set per-screen by the server, and are always integers (most often 1 or 2).
//! - **iOS:** DPI factors are both constant and device-specific on iOS.
//! - **Android:** This feature isn't yet implemented on Android, so the DPI factor will always be returned as 1.0.
//!
//! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes produce a
//! may be surprising on X11, but is quite standard elsewhere. Physical size changes always produce a
//! [`Resized`](../enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs,
//! such as macOS and Wayland. As a result, it's not necessary to separately handle
//! [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size.
//!
//! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your
//! framebuffer's size should be in physical pixels.
//!
//! `winit` will send [`Resized`](../enum.WindowEvent.html#variant.Resized) events whenever a window's logical size
//! changes, and [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) events
//! whenever the DPI factor changes. Receiving either of these events means that the physical size of your window has
//! changed, and you should recompute it using the latest values you received for each. If the logical size and the
//! DPI factor change simultaneously, `winit` will send both events together; thus, it's recommended to buffer
//! these events and process them at the end of the queue.
//!
//! If you never received any [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) events,
//! then your window's DPI factor is 1.
/// Checks that the DPI factor is a normal positive `f64`.
///
@@ -82,6 +92,7 @@ pub fn validate_hidpi_factor(dpi_factor: f64) -> bool {
/// which can cause noticable issues. To help with that, an `Into<(i32, i32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalPosition {
pub x: f64,
pub y: f64,
@@ -142,6 +153,7 @@ impl Into<(i32, i32)> for LogicalPosition {
/// which can cause noticable issues. To help with that, an `Into<(i32, i32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalPosition {
pub x: f64,
pub y: f64,
@@ -202,6 +214,7 @@ impl Into<(i32, i32)> for PhysicalPosition {
/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LogicalSize {
pub width: f64,
pub height: f64,
@@ -262,6 +275,7 @@ impl Into<(u32, u32)> for LogicalSize {
/// which can cause noticable issues. To help with that, an `Into<(u32, u32)>` implementation is provided which
/// does the rounding for you.
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PhysicalSize {
pub width: f64,
pub height: f64,

View File

@@ -3,7 +3,7 @@ use std::path::PathBuf;
use {DeviceId, LogicalPosition, LogicalSize, WindowId};
/// Describes a generic event.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub enum Event {
WindowEvent {
window_id: WindowId,
@@ -22,7 +22,7 @@ pub enum Event {
}
/// Describes an event from a `Window`.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub enum WindowEvent {
/// The size of the window has changed. Contains the client area's new dimensions.
Resized(LogicalSize),
@@ -116,7 +116,7 @@ pub enum WindowEvent {
/// may not match.
///
/// Note that these events are delivered regardless of input focus.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub enum DeviceEvent {
Added,
Removed,
@@ -147,7 +147,8 @@ pub enum DeviceEvent {
}
/// Describes a keyboard input event.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct KeyboardInput {
/// Identifies the physical key pressed
///
@@ -173,6 +174,7 @@ pub struct KeyboardInput {
/// Describes touch-screen input state.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TouchPhase {
Started,
Moved,
@@ -195,7 +197,7 @@ pub enum TouchPhase {
/// as previously received End event is a new finger and has nothing to do with an old one.
///
/// Touch may be cancelled if for example window lost focus.
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Touch {
pub device_id: DeviceId,
pub phase: TouchPhase,
@@ -215,6 +217,7 @@ pub type ButtonId = u32;
/// Describes the input state of a key.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ElementState {
Pressed,
Released,
@@ -222,6 +225,7 @@ pub enum ElementState {
/// Describes a button of a mouse controller.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseButton {
Left,
Right,
@@ -231,6 +235,7 @@ pub enum MouseButton {
/// Describes a difference in the mouse scroll wheel state.
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseScrollDelta {
/// Amount in lines or rows to scroll in the horizontal
/// and vertical directions.
@@ -250,6 +255,7 @@ pub enum MouseScrollDelta {
/// Symbolic name for a keyboard key.
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum VirtualKeyCode {
/// The '1' key over the letters.
Key1,
@@ -317,6 +323,15 @@ pub enum VirtualKeyCode {
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
/// Print Screen/SysRq.
Snapshot,
@@ -440,6 +455,8 @@ pub enum VirtualKeyCode {
///
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
#[derive(Default, Debug, Hash, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct ModifiersState {
/// The "shift" key
pub shift: bool,

View File

@@ -146,7 +146,7 @@ impl Icon {
/// Requires the `icon_loading` feature.
impl From<image::DynamicImage> for Icon {
fn from(image: image::DynamicImage) -> Self {
use image::{GenericImage, Pixel};
use image::{GenericImageView, Pixel};
let (width, height) = image.dimensions();
let mut rgba = Vec::with_capacity((width * height) as usize * PIXEL_SIZE);
for (_, _, pixel) in image.pixels() {

View File

@@ -92,6 +92,9 @@ extern crate libc;
extern crate log;
#[cfg(feature = "icon_loading")]
extern crate image;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
#[cfg(target_os = "windows")]
extern crate winapi;
@@ -149,6 +152,12 @@ pub struct Window {
window: platform::Window,
}
impl std::fmt::Debug for Window {
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
fmtr.pad("Window { .. }")
}
}
/// Identifier of a window. Unique for each window.
///
/// Can be obtained with `window.id()`.
@@ -184,10 +193,17 @@ pub struct EventsLoop {
_marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync
}
impl std::fmt::Debug for EventsLoop {
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
fmtr.pad("EventsLoop { .. }")
}
}
/// Returned by the user callback given to the `EventsLoop::run_forever` method.
///
/// Indicates whether the `run_forever` method should continue or complete.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ControlFlow {
/// Continue looping and waiting for events.
Continue,
@@ -263,6 +279,12 @@ pub struct EventsLoopProxy {
events_loop_proxy: platform::EventsLoopProxy,
}
impl std::fmt::Debug for EventsLoopProxy {
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
fmtr.pad("EventsLoopProxy { .. }")
}
}
impl EventsLoopProxy {
/// Wake up the `EventsLoop` from which this proxy was created.
///
@@ -301,6 +323,14 @@ pub struct WindowBuilder {
platform_specific: platform::PlatformSpecificWindowBuilderAttributes,
}
impl std::fmt::Debug for WindowBuilder {
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
fmtr.debug_struct("WindowBuilder")
.field("window", &self.window)
.finish()
}
}
/// Error that can happen while creating a window or a headless renderer.
#[derive(Debug, Clone)]
pub enum CreationError {
@@ -331,7 +361,8 @@ impl std::error::Error for CreationError {
}
/// Describes the appearance of the mouse cursor.
#[derive(Debug, Copy, Clone, PartialEq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MouseCursor {
/// The platform-dependent default cursor.
Default,

View File

@@ -1,8 +1,6 @@
#![cfg(target_os = "macos")]
use std::convert::From;
use std::os::raw::c_void;
use cocoa::appkit::NSApplicationActivationPolicy;
use {LogicalSize, MonitorId, Window, WindowBuilder};
/// Additional methods on `Window` that are specific to MacOS.
@@ -16,6 +14,14 @@ pub trait WindowExt {
///
/// The pointer will become invalid when the `Window` is destroyed.
fn get_nsview(&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.
///
/// The `is_critical` flag has the following effects:
/// - `false`: the dock icon will only bounce once.
/// - `true`: the dock icon will bounce until the application is focused.
fn request_user_attention(&self, is_critical: bool);
}
impl WindowExt for Window {
@@ -28,6 +34,11 @@ impl WindowExt for Window {
fn get_nsview(&self) -> *mut c_void {
self.window.get_nsview()
}
#[inline]
fn request_user_attention(&self, is_critical: bool) {
self.window.request_user_attention(is_critical)
}
}
/// Corresponds to `NSApplicationActivationPolicy`.
@@ -47,19 +58,6 @@ impl Default for ActivationPolicy {
}
}
impl From<ActivationPolicy> for NSApplicationActivationPolicy {
fn from(activation_policy: ActivationPolicy) -> Self {
match activation_policy {
ActivationPolicy::Regular =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
ActivationPolicy::Accessory =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory,
ActivationPolicy::Prohibited =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited,
}
}
}
/// Additional methods on `WindowBuilder` that are specific to MacOS.
///
/// **Note:** Properties dealing with the titlebar will be overwritten by the `with_decorations` method

View File

@@ -219,6 +219,8 @@ pub trait WindowBuilderExt {
fn with_override_redirect(self, override_redirect: bool) -> WindowBuilder;
/// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11.
fn with_x11_window_type(self, x11_window_type: XWindowType) -> WindowBuilder;
/// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11.
fn with_gtk_theme_variant(self, variant: String) -> WindowBuilder;
/// Build window with resize increment hint. Only implemented on X11.
fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder;
/// Build window with base size hint. Only implemented on X11.
@@ -269,6 +271,12 @@ impl WindowBuilderExt for WindowBuilder {
self.platform_specific.base_size = Some(base_size.into());
self
}
#[inline]
fn with_gtk_theme_variant(mut self, variant: String) -> WindowBuilder {
self.platform_specific.gtk_theme_variant = Some(variant);
self
}
}
/// Additional methods on `MonitorId` that are specific to Linux.

View File

@@ -810,11 +810,15 @@ fn key_translate_virt(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES
"F13" => Some(F13),
"F14" => Some(F14),
"F15" => Some(F15),
"F16" => None,
"F17" => None,
"F18" => None,
"F19" => None,
"F20" => None,
"F16" => Some(F16),
"F17" => Some(F17),
"F18" => Some(F18),
"F19" => Some(F19),
"F20" => Some(F20),
"F21" => Some(F21),
"F22" => Some(F22),
"F23" => Some(F23),
"F24" => Some(F24),
"Soft1" => None,
"Soft2" => None,
"Soft3" => None,

View File

@@ -45,6 +45,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub class: Option<(String, String)>,
pub override_redirect: bool,
pub x11_window_type: x11::util::WindowType,
pub gtk_theme_variant: Option<String>,
}
lazy_static!(
@@ -429,10 +430,7 @@ impl EventsLoop {
};
let err_string = format!(
r#"Failed to initialize any backend!
Wayland status: {:#?}
X11 status: {:#?}
"#,
"Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}",
wayland_err,
x11_err,
);

View File

@@ -1,22 +1,25 @@
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use std::sync::{Arc, Mutex, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, Weak};
use {ControlFlow, EventsLoopClosed, PhysicalPosition, PhysicalSize};
use super::WindowId;
use super::window::WindowStore;
use super::WindowId;
use sctk::Environment;
use sctk::output::OutputMgr;
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, Proxy, ConnectError};
use sctk::reexports::client::commons::Implementation;
use sctk::reexports::client::protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat,
wl_touch};
use sctk::reexports::client::protocol::{
wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch,
};
use sctk::reexports::client::{ConnectError, Display, EventQueue, GlobalEvent, Proxy};
use sctk::Environment;
use sctk::reexports::client::protocol::wl_display::RequestsTrait as DisplayRequests;
use sctk::reexports::client::protocol::wl_surface::RequestsTrait;
use ModifiersState;
pub struct EventsLoopSink {
buffer: VecDeque<::Event>,
@@ -91,7 +94,7 @@ impl EventsLoopProxy {
// Update the `EventsLoop`'s `pending_wakeup` flag.
wakeup.store(true, Ordering::Relaxed);
// Cause the `EventsLoop` to break from `dispatch` if it is currently blocked.
let _ = display.sync();
let _ = display.sync(|callback| callback.implement(|_, _| {}, ()));
display.flush().map_err(|_| EventsLoopClosed)?;
Ok(())
}
@@ -104,29 +107,39 @@ impl EventsLoop {
pub fn new() -> Result<EventsLoop, ConnectError> {
let (display, mut event_queue) = Display::connect_to_env()?;
let display = Arc::new(display);
let pending_wakeup = Arc::new(AtomicBool::new(false));
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
let store = Arc::new(Mutex::new(WindowStore::new()));
let seats = Arc::new(Mutex::new(Vec::new()));
let env = Environment::from_registry_with_cb(
display.get_registry().unwrap(),
let mut seat_manager = SeatManager {
sink: sink.clone(),
store: store.clone(),
seats: seats.clone(),
events_loop_proxy: EventsLoopProxy {
display: Arc::downgrade(&display),
pending_wakeup: Arc::downgrade(&pending_wakeup),
},
};
let env = Environment::from_display_with_cb(
&display,
&mut event_queue,
SeatManager {
sink: sink.clone(),
store: store.clone(),
seats: seats.clone(),
move |event, registry| {
seat_manager.receive(event, registry)
},
).unwrap();
Ok(EventsLoop {
display: Arc::new(display),
display,
evq: RefCell::new(event_queue),
sink: sink,
pending_wakeup: Arc::new(AtomicBool::new(false)),
store: store,
env: env,
sink,
pending_wakeup,
store,
env,
cleanup_needed: Arc::new(Mutex::new(false)),
seats: seats,
seats,
})
}
@@ -242,6 +255,9 @@ impl EventsLoop {
*size = (w, h);
} else if frame_refresh {
frame.refresh();
if !refresh {
frame.surface().commit()
}
}
}
if let Some(dpi) = new_dpi {
@@ -266,9 +282,10 @@ struct SeatManager {
sink: Arc<Mutex<EventsLoopSink>>,
store: Arc<Mutex<WindowStore>>,
seats: Arc<Mutex<Vec<(u32, Proxy<wl_seat::WlSeat>)>>>,
events_loop_proxy: EventsLoopProxy,
}
impl Implementation<Proxy<wl_registry::WlRegistry>, GlobalEvent> for SeatManager {
impl SeatManager {
fn receive(&mut self, evt: GlobalEvent, registry: Proxy<wl_registry::WlRegistry>) {
use self::wl_registry::RequestsTrait as RegistryRequests;
use self::wl_seat::RequestsTrait as SeatRequests;
@@ -280,16 +297,23 @@ impl Implementation<Proxy<wl_registry::WlRegistry>, GlobalEvent> for SeatManager
} if interface == "wl_seat" =>
{
use std::cmp::min;
let mut seat_data = SeatData {
sink: self.sink.clone(),
store: self.store.clone(),
pointer: None,
keyboard: None,
touch: None,
events_loop_proxy: self.events_loop_proxy.clone(),
modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())),
};
let seat = registry
.bind::<wl_seat::WlSeat>(min(version, 5), id)
.unwrap()
.implement(SeatData {
sink: self.sink.clone(),
store: self.store.clone(),
pointer: None,
keyboard: None,
touch: None,
});
.bind(min(version, 5), id, move |seat| {
seat.implement(move |event, seat| {
seat_data.receive(event, seat)
}, ())
})
.unwrap();
self.store.lock().unwrap().new_seat(&seat);
self.seats.lock().unwrap().push((id, seat));
}
@@ -313,20 +337,22 @@ struct SeatData {
pointer: Option<Proxy<wl_pointer::WlPointer>>,
keyboard: Option<Proxy<wl_keyboard::WlKeyboard>>,
touch: Option<Proxy<wl_touch::WlTouch>>,
events_loop_proxy: EventsLoopProxy,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
}
impl Implementation<Proxy<wl_seat::WlSeat>, wl_seat::Event> for SeatData {
impl SeatData {
fn receive(&mut self, evt: wl_seat::Event, seat: Proxy<wl_seat::WlSeat>) {
use self::wl_seat::RequestsTrait as SeatRequests;
match evt {
wl_seat::Event::Name { .. } => (),
wl_seat::Event::Capabilities { capabilities } => {
// create pointer if applicable
if capabilities.contains(wl_seat::Capability::Pointer) && self.pointer.is_none() {
self.pointer = Some(super::pointer::implement_pointer(
seat.get_pointer().unwrap(),
&seat,
self.sink.clone(),
self.store.clone(),
self.modifiers_tracker.clone(),
))
}
// destroy pointer if applicable
@@ -341,8 +367,10 @@ impl Implementation<Proxy<wl_seat::WlSeat>, wl_seat::Event> for SeatData {
// create keyboard if applicable
if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() {
self.keyboard = Some(super::keyboard::init_keyboard(
seat.get_keyboard().unwrap(),
&seat,
self.sink.clone(),
self.events_loop_proxy.clone(),
self.modifiers_tracker.clone(),
))
}
// destroy keyboard if applicable
@@ -357,7 +385,7 @@ impl Implementation<Proxy<wl_seat::WlSeat>, wl_seat::Event> for SeatData {
// create touch if applicable
if capabilities.contains(wl_seat::Capability::Touch) && self.touch.is_none() {
self.touch = Some(super::touch::implement_touch(
seat.get_touch().unwrap(),
&seat,
self.sink.clone(),
self.store.clone(),
))

View File

@@ -1,81 +1,121 @@
use std::sync::{Arc, Mutex};
use super::{make_wid, DeviceId, EventsLoopProxy, EventsLoopSink};
use sctk::keyboard::{
self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind,
};
use sctk::reexports::client::protocol::wl_keyboard;
use sctk::reexports::client::Proxy;
use sctk::reexports::client::protocol::wl_seat;
use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests;
use {ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
use super::{make_wid, DeviceId, EventsLoopSink};
use sctk::keyboard::{self, map_keyboard_auto, Event as KbEvent};
use sctk::reexports::client::{NewProxy, Proxy};
use sctk::reexports::client::protocol::wl_keyboard;
pub fn init_keyboard(
keyboard: NewProxy<wl_keyboard::WlKeyboard>,
seat: &Proxy<wl_seat::WlSeat>,
sink: Arc<Mutex<EventsLoopSink>>,
events_loop_proxy: EventsLoopProxy,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
) -> Proxy<wl_keyboard::WlKeyboard> {
// { variables to be captured by the closure
let mut target = None;
// { variables to be captured by the closures
let target = Arc::new(Mutex::new(None));
let my_sink = sink.clone();
let repeat_sink = sink.clone();
let repeat_target = target.clone();
let my_modifiers = modifiers_tracker.clone();
// }
let ret = map_keyboard_auto(keyboard, move |evt: KbEvent, _| match evt {
KbEvent::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.lock()
.unwrap()
.send_event(WindowEvent::Focused(true), wid);
target = Some(wid);
}
KbEvent::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.lock()
.unwrap()
.send_event(WindowEvent::Focused(false), wid);
target = None;
}
KbEvent::Key {
modifiers,
rawkey,
keysym,
state,
utf8,
..
} => {
if let Some(wid) = target {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
};
let vkcode = key_to_vkey(rawkey, keysym);
let mut guard = my_sink.lock().unwrap();
let ret = map_keyboard_auto_with_repeat(
seat,
KeyRepeatKind::System,
move |evt: KbEvent, _| match evt {
KbEvent::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.lock()
.unwrap()
.send_event(WindowEvent::Focused(true), wid);
*target.lock().unwrap() = Some(wid);
}
KbEvent::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.lock()
.unwrap()
.send_event(WindowEvent::Focused(false), wid);
*target.lock().unwrap() = None;
}
KbEvent::Key {
rawkey,
keysym,
state,
utf8,
..
} => {
if let Some(wid) = *target.lock().unwrap() {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
};
let vkcode = key_to_vkey(rawkey, keysym);
let mut guard = my_sink.lock().unwrap();
guard.send_event(
WindowEvent::KeyboardInput {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
input: KeyboardInput {
state: state,
scancode: rawkey,
virtual_keycode: vkcode,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
},
wid,
);
// send char event only on key press, not release
if let ElementState::Released = state {
return;
}
if let Some(txt) = utf8 {
for chr in txt.chars() {
guard.send_event(WindowEvent::ReceivedCharacter(chr), wid);
}
}
}
}
KbEvent::RepeatInfo { .. } => { /* Handled by smithay client toolkit */ }
KbEvent::Modifiers { modifiers: event_modifiers } => {
*modifiers_tracker.lock().unwrap() = event_modifiers.into()
}
},
move |repeat_event: KeyRepeatEvent, _| {
if let Some(wid) = *repeat_target.lock().unwrap() {
let state = ElementState::Pressed;
let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym);
let mut guard = repeat_sink.lock().unwrap();
guard.send_event(
WindowEvent::KeyboardInput {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
input: KeyboardInput {
state: state,
scancode: rawkey,
scancode: repeat_event.rawkey,
virtual_keycode: vkcode,
modifiers: modifiers.into(),
modifiers: my_modifiers.lock().unwrap().clone(),
},
},
wid,
);
// send char event only on key press, not release
if let ElementState::Released = state {
return;
}
if let Some(txt) = utf8 {
if let Some(txt) = repeat_event.utf8 {
for chr in txt.chars() {
guard.send_event(WindowEvent::ReceivedCharacter(chr), wid);
}
}
events_loop_proxy.wakeup().unwrap();
}
}
KbEvent::RepeatInfo { .. } => { /* TODO: handle repeat info */ }
});
},
);
match ret {
Ok(keyboard) => keyboard,
Err((_, keyboard)) => {
Err(_) => {
// This is a fallback impl if libxkbcommon was not available
// This case should probably never happen, as most wayland
// compositors _need_ libxkbcommon anyway...
@@ -87,45 +127,47 @@ pub fn init_keyboard(
let mut target = None;
let my_sink = sink;
// }
keyboard.implement(move |evt, _| match evt {
wl_keyboard::Event::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.lock()
.unwrap()
.send_event(WindowEvent::Focused(true), wid);
target = Some(wid);
}
wl_keyboard::Event::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.lock()
.unwrap()
.send_event(WindowEvent::Focused(false), wid);
target = None;
}
wl_keyboard::Event::Key { key, state, .. } => {
if let Some(wid) = target {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
};
my_sink.lock().unwrap().send_event(
WindowEvent::KeyboardInput {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
input: KeyboardInput {
state: state,
scancode: key,
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
},
wid,
);
seat.get_keyboard(|keyboard| {
keyboard.implement(move |evt, _| match evt {
wl_keyboard::Event::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.lock()
.unwrap()
.send_event(WindowEvent::Focused(true), wid);
target = Some(wid);
}
}
_ => (),
})
wl_keyboard::Event::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink
.lock()
.unwrap()
.send_event(WindowEvent::Focused(false), wid);
target = None;
}
wl_keyboard::Event::Key { key, state, .. } => {
if let Some(wid) = target {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
};
my_sink.lock().unwrap().send_event(
WindowEvent::KeyboardInput {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
input: KeyboardInput {
state: state,
scancode: key,
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
},
wid,
);
}
}
_ => (),
}, ())
}).unwrap()
}
}
}
@@ -193,6 +235,15 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13),
keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14),
keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15),
keysyms::XKB_KEY_F16 => Some(VirtualKeyCode::F16),
keysyms::XKB_KEY_F17 => Some(VirtualKeyCode::F17),
keysyms::XKB_KEY_F18 => Some(VirtualKeyCode::F18),
keysyms::XKB_KEY_F19 => Some(VirtualKeyCode::F19),
keysyms::XKB_KEY_F20 => Some(VirtualKeyCode::F20),
keysyms::XKB_KEY_F21 => Some(VirtualKeyCode::F21),
keysyms::XKB_KEY_F22 => Some(VirtualKeyCode::F22),
keysyms::XKB_KEY_F23 => Some(VirtualKeyCode::F23),
keysyms::XKB_KEY_F24 => Some(VirtualKeyCode::F24),
// flow control
keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot),
keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll),

View File

@@ -7,184 +7,183 @@ use super::DeviceId;
use super::event_loop::EventsLoopSink;
use super::window::WindowStore;
use sctk::reexports::client::{NewProxy, Proxy};
use sctk::reexports::client::Proxy;
use sctk::reexports::client::protocol::wl_pointer::{self, Event as PtrEvent, WlPointer};
use sctk::reexports::client::protocol::wl_seat;
use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests;
pub fn implement_pointer(
pointer: NewProxy<WlPointer>,
seat: &Proxy<wl_seat::WlSeat>,
sink: Arc<Mutex<EventsLoopSink>>,
store: Arc<Mutex<WindowStore>>,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
) -> Proxy<WlPointer> {
let mut mouse_focus = None;
let mut axis_buffer = None;
let mut axis_discrete_buffer = None;
let mut axis_state = TouchPhase::Ended;
pointer.implement(move |evt, pointer: Proxy<_>| {
let mut sink = sink.lock().unwrap();
let store = store.lock().unwrap();
match evt {
PtrEvent::Enter {
surface,
surface_x,
surface_y,
..
} => {
let wid = store.find_wid(&surface);
if let Some(wid) = wid {
mouse_focus = Some(wid);
sink.send_event(
WindowEvent::CursorEntered {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
},
wid,
);
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y).into(),
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
wid,
);
}
}
PtrEvent::Leave { surface, .. } => {
mouse_focus = None;
let wid = store.find_wid(&surface);
if let Some(wid) = wid {
sink.send_event(
WindowEvent::CursorLeft {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
},
wid,
);
}
}
PtrEvent::Motion {
surface_x,
surface_y,
..
} => {
if let Some(wid) = mouse_focus {
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y).into(),
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
wid,
);
}
}
PtrEvent::Button { button, state, .. } => {
if let Some(wid) = mouse_focus {
let state = match state {
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
wl_pointer::ButtonState::Released => ElementState::Released,
};
let button = match button {
0x110 => MouseButton::Left,
0x111 => MouseButton::Right,
0x112 => MouseButton::Middle,
// TODO figure out the translation ?
_ => return,
};
sink.send_event(
WindowEvent::MouseInput {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
state: state,
button: button,
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
wid,
);
}
}
PtrEvent::Axis { axis, value, .. } => {
if let Some(wid) = mouse_focus {
if pointer.version() < 5 {
let (mut x, mut y) = (0.0, 0.0);
// old seat compatibility
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
}
seat.get_pointer(|pointer| {
pointer.implement(move |evt, pointer| {
let mut sink = sink.lock().unwrap();
let store = store.lock().unwrap();
match evt {
PtrEvent::Enter {
surface,
surface_x,
surface_y,
..
} => {
let wid = store.find_wid(&surface);
if let Some(wid) = wid {
mouse_focus = Some(wid);
sink.send_event(
WindowEvent::MouseWheel {
WindowEvent::CursorEntered {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: TouchPhase::Moved,
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
wid,
);
} else {
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0));
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
}
axis_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
}
}
PtrEvent::Frame => {
let axis_buffer = axis_buffer.take();
let axis_discrete_buffer = axis_discrete_buffer.take();
if let Some(wid) = mouse_focus {
if let Some((x, y)) = axis_discrete_buffer {
sink.send_event(
WindowEvent::MouseWheel {
WindowEvent::CursorMoved {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: axis_state,
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
},
wid,
);
} else if let Some((x, y)) = axis_buffer {
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: axis_state,
// TODO: replace dummy value with actual modifier state
modifiers: ModifiersState::default(),
position: (surface_x, surface_y).into(),
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
}
PtrEvent::AxisSource { .. } => (),
PtrEvent::AxisStop { .. } => {
axis_state = TouchPhase::Ended;
}
PtrEvent::AxisDiscrete { axis, discrete } => {
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0, 0));
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= discrete,
wl_pointer::Axis::HorizontalScroll => x += discrete,
PtrEvent::Leave { surface, .. } => {
mouse_focus = None;
let wid = store.find_wid(&surface);
if let Some(wid) = wid {
sink.send_event(
WindowEvent::CursorLeft {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
},
wid,
);
}
}
axis_discrete_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
PtrEvent::Motion {
surface_x,
surface_y,
..
} => {
if let Some(wid) = mouse_focus {
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y).into(),
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
PtrEvent::Button { button, state, .. } => {
if let Some(wid) = mouse_focus {
let state = match state {
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
wl_pointer::ButtonState::Released => ElementState::Released,
};
let button = match button {
0x110 => MouseButton::Left,
0x111 => MouseButton::Right,
0x112 => MouseButton::Middle,
// TODO figure out the translation ?
_ => return,
};
sink.send_event(
WindowEvent::MouseInput {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
state: state,
button: button,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
PtrEvent::Axis { axis, value, .. } => {
if let Some(wid) = mouse_focus {
if pointer.version() < 5 {
let (mut x, mut y) = (0.0, 0.0);
// old seat compatibility
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
}
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: TouchPhase::Moved,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
} else {
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0));
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= value as f32,
wl_pointer::Axis::HorizontalScroll => x += value as f32,
}
axis_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
}
}
PtrEvent::Frame => {
let axis_buffer = axis_buffer.take();
let axis_discrete_buffer = axis_discrete_buffer.take();
if let Some(wid) = mouse_focus {
if let Some((x, y)) = axis_discrete_buffer {
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
} else if let Some((x, y)) = axis_buffer {
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
}
PtrEvent::AxisSource { .. } => (),
PtrEvent::AxisStop { .. } => {
axis_state = TouchPhase::Ended;
}
PtrEvent::AxisDiscrete { axis, discrete } => {
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0, 0));
match axis {
// wayland vertical sign convention is the inverse of winit
wl_pointer::Axis::VerticalScroll => y -= discrete,
wl_pointer::Axis::HorizontalScroll => x += discrete,
}
axis_discrete_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
}
}
})
}, ())
}).unwrap()
}

View File

@@ -6,8 +6,10 @@ use super::{DeviceId, WindowId};
use super::event_loop::EventsLoopSink;
use super::window::WindowStore;
use sctk::reexports::client::{NewProxy, Proxy};
use sctk::reexports::client::Proxy;
use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch};
use sctk::reexports::client::protocol::wl_seat;
use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests;
struct TouchPoint {
wid: WindowId,
@@ -16,78 +18,80 @@ struct TouchPoint {
}
pub(crate) fn implement_touch(
touch: NewProxy<WlTouch>,
seat: &Proxy<wl_seat::WlSeat>,
sink: Arc<Mutex<EventsLoopSink>>,
store: Arc<Mutex<WindowStore>>,
) -> Proxy<WlTouch> {
let mut pending_ids = Vec::new();
touch.implement(move |evt, _| {
let mut sink = sink.lock().unwrap();
let store = store.lock().unwrap();
match evt {
TouchEvent::Down {
surface, id, x, y, ..
} => {
let wid = store.find_wid(&surface);
if let Some(wid) = wid {
sink.send_event(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Started,
location: (x, y).into(),
id: id as u64,
}),
wid,
);
pending_ids.push(TouchPoint {
wid: wid,
location: (x, y),
id: id,
});
seat.get_touch(|touch| {
touch.implement(move |evt, _| {
let mut sink = sink.lock().unwrap();
let store = store.lock().unwrap();
match evt {
TouchEvent::Down {
surface, id, x, y, ..
} => {
let wid = store.find_wid(&surface);
if let Some(wid) = wid {
sink.send_event(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Started,
location: (x, y).into(),
id: id as u64,
}),
wid,
);
pending_ids.push(TouchPoint {
wid: wid,
location: (x, y),
id: id,
});
}
}
}
TouchEvent::Up { id, .. } => {
let idx = pending_ids.iter().position(|p| p.id == id);
if let Some(idx) = idx {
let pt = pending_ids.remove(idx);
TouchEvent::Up { id, .. } => {
let idx = pending_ids.iter().position(|p| p.id == id);
if let Some(idx) = idx {
let pt = pending_ids.remove(idx);
sink.send_event(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Ended,
location: pt.location.into(),
id: id as u64,
}),
pt.wid,
);
}
}
TouchEvent::Motion { id, x, y, .. } => {
let pt = pending_ids.iter_mut().find(|p| p.id == id);
if let Some(pt) = pt {
pt.location = (x, y);
sink.send_event(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Moved,
location: (x, y).into(),
id: id as u64,
}),
pt.wid,
);
}
}
TouchEvent::Frame => (),
TouchEvent::Cancel => for pt in pending_ids.drain(..) {
sink.send_event(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Ended,
phase: TouchPhase::Cancelled,
location: pt.location.into(),
id: id as u64,
id: pt.id as u64,
}),
pt.wid,
);
}
},
}
TouchEvent::Motion { id, x, y, .. } => {
let pt = pending_ids.iter_mut().find(|p| p.id == id);
if let Some(pt) = pt {
pt.location = (x, y);
sink.send_event(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Moved,
location: (x, y).into(),
id: id as u64,
}),
pt.wid,
);
}
}
TouchEvent::Frame => (),
TouchEvent::Cancel => for pt in pending_ids.drain(..) {
sink.send_event(
WindowEvent::Touch(::Touch {
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Cancelled,
location: pt.location.into(),
id: pt.id as u64,
}),
pt.wid,
);
},
}
})
}, ())
}).unwrap()
}

View File

@@ -6,7 +6,7 @@ use dpi::{LogicalPosition, LogicalSize};
use platform::MonitorId as PlatformMonitorId;
use window::MonitorId as RootMonitorId;
use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow};
use sctk::window::{ConceptFrame, Event as WEvent, Window as SWindow};
use sctk::reexports::client::{Display, Proxy};
use sctk::reexports::client::protocol::{wl_seat, wl_surface, wl_output};
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
@@ -18,7 +18,7 @@ use platform::platform::wayland::event_loop::{get_available_monitors, get_primar
pub struct Window {
surface: Proxy<wl_surface::WlSurface>,
frame: Arc<Mutex<SWindow<BasicFrame>>>,
frame: Arc<Mutex<SWindow<ConceptFrame>>>,
monitors: Arc<Mutex<MonitorList>>, // Monitors this window is currently on
outputs: OutputMgr, // Access to info for all monitors
size: Arc<Mutex<(u32, u32)>>,
@@ -36,11 +36,11 @@ impl Window {
// monitor tracking
let monitor_list = Arc::new(Mutex::new(MonitorList::new()));
let surface = evlp.env.compositor.create_surface().unwrap().implement({
let surface = evlp.env.compositor.create_surface(|surface| {
let list = monitor_list.clone();
let omgr = evlp.env.outputs.clone();
let window_store = evlp.store.clone();
move |event, surface: Proxy<wl_surface::WlSurface>| match event {
surface.implement(move |event, surface| match event {
wl_surface::Event::Enter { output } => {
let dpi_change = list.lock().unwrap().add_output(MonitorId {
proxy: output,
@@ -64,19 +64,16 @@ impl Window {
}
}
}
}
});
}, ())
}).unwrap();
let window_store = evlp.store.clone();
let my_surface = surface.clone();
let mut frame = SWindow::<BasicFrame>::init(
let mut frame = SWindow::<ConceptFrame>::init_from_env(
&evlp.env,
surface.clone(),
(width, height),
&evlp.env.compositor,
&evlp.env.subcompositor,
&evlp.env.shm,
&evlp.env.shell,
move |event, ()| match event {
move |event| match event {
WEvent::Configure { new_size, .. } => {
let mut store = window_store.lock().unwrap();
for window in &mut store.windows {
@@ -327,7 +324,7 @@ struct InternalWindow {
need_frame_refresh: Arc<Mutex<bool>>,
closed: bool,
kill_switch: Arc<Mutex<bool>>,
frame: Weak<Mutex<SWindow<BasicFrame>>>,
frame: Weak<Mutex<SWindow<ConceptFrame>>>,
current_dpi: i32,
new_dpi: Option<i32>
}
@@ -385,7 +382,7 @@ impl WindowStore {
pub fn for_each<F>(&mut self, mut f: F)
where
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<ConceptFrame>>),
{
for window in &mut self.windows {
let opt_arc = window.frame.upgrade();

View File

@@ -117,23 +117,23 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
//ffi::XK_L4 => events::VirtualKeyCode::L4,
ffi::XK_F15 => events::VirtualKeyCode::F15,
//ffi::XK_L5 => events::VirtualKeyCode::L5,
//ffi::XK_F16 => events::VirtualKeyCode::F16,
ffi::XK_F16 => events::VirtualKeyCode::F16,
//ffi::XK_L6 => events::VirtualKeyCode::L6,
//ffi::XK_F17 => events::VirtualKeyCode::F17,
ffi::XK_F17 => events::VirtualKeyCode::F17,
//ffi::XK_L7 => events::VirtualKeyCode::L7,
//ffi::XK_F18 => events::VirtualKeyCode::F18,
ffi::XK_F18 => events::VirtualKeyCode::F18,
//ffi::XK_L8 => events::VirtualKeyCode::L8,
//ffi::XK_F19 => events::VirtualKeyCode::F19,
ffi::XK_F19 => events::VirtualKeyCode::F19,
//ffi::XK_L9 => events::VirtualKeyCode::L9,
//ffi::XK_F20 => events::VirtualKeyCode::F20,
ffi::XK_F20 => events::VirtualKeyCode::F20,
//ffi::XK_L10 => events::VirtualKeyCode::L10,
//ffi::XK_F21 => events::VirtualKeyCode::F21,
ffi::XK_F21 => events::VirtualKeyCode::F21,
//ffi::XK_R1 => events::VirtualKeyCode::R1,
//ffi::XK_F22 => events::VirtualKeyCode::F22,
ffi::XK_F22 => events::VirtualKeyCode::F22,
//ffi::XK_R2 => events::VirtualKeyCode::R2,
//ffi::XK_F23 => events::VirtualKeyCode::F23,
ffi::XK_F23 => events::VirtualKeyCode::F23,
//ffi::XK_R3 => events::VirtualKeyCode::R3,
//ffi::XK_F24 => events::VirtualKeyCode::F24,
ffi::XK_F24 => events::VirtualKeyCode::F24,
//ffi::XK_R4 => events::VirtualKeyCode::R4,
//ffi::XK_F25 => events::VirtualKeyCode::F25,
//ffi::XK_R5 => events::VirtualKeyCode::R5,

View File

@@ -278,25 +278,25 @@ impl EventsLoop {
}
} else if client_msg.message_type == self.dnd.atoms.position {
// This event occurs every time the mouse moves while a file's being dragged
// over our window. We emit HoveredFile in response; while the Mac OS X backend
// does that upon a drag entering, XDnD doesn't have access to the actual drop
// over our window. We emit HoveredFile in response; while the macOS backend
// does that upon a drag entering, XDND doesn't have access to the actual drop
// data until this event. For parity with other platforms, we only emit
// HoveredFile the first time, though if winit's API is later extended to
// supply position updates with HoveredFile or another event, implementing
// `HoveredFile` the first time, though if winit's API is later extended to
// supply position updates with `HoveredFile` or another event, implementing
// that here would be trivial.
let source_window = client_msg.data.get_long(0) as c_ulong;
// Equivalent to (x << shift) | y
// where shift = mem::size_of::<c_short>() * 8
// Equivalent to `(x << shift) | y`
// where `shift = mem::size_of::<c_short>() * 8`
// Note that coordinates are in "desktop space", not "window space"
// (in x11 parlance, they're root window coordinates)
// (in X11 parlance, they're root window coordinates)
//let packed_coordinates = client_msg.data.get_long(2);
//let shift = mem::size_of::<libc::c_short>() * 8;
//let x = packed_coordinates >> shift;
//let y = packed_coordinates & !(x << shift);
// By our own state flow, version should never be None at this point.
// By our own state flow, `version` should never be `None` at this point.
let version = self.dnd.version.unwrap_or(5);
// Action is specified in versions 2 and up, though we don't need it anyway.
@@ -318,23 +318,21 @@ impl EventsLoop {
// In version 0, time isn't specified
ffi::CurrentTime
};
// This results in the SelectionNotify event below
// This results in the `SelectionNotify` event below
self.dnd.convert_selection(window, time);
}
self.dnd.send_status(window, source_window, DndState::Accepted)
.expect("Failed to send XDnD status message.");
.expect("Failed to send `XdndStatus` message.");
}
} else {
unsafe {
self.dnd.send_status(window, source_window, DndState::Rejected)
.expect("Failed to send XDnD status message.");
self.dnd.send_finished(window, source_window, DndState::Rejected)
.expect("Failed to send XDnD finished message.");
.expect("Failed to send `XdndStatus` message.");
}
self.dnd.reset();
}
} else if client_msg.message_type == self.dnd.atoms.drop {
if let Some(source_window) = self.dnd.source_window {
let (source_window, state) = if let Some(source_window) = self.dnd.source_window {
if let Some(Ok(ref path_list)) = self.dnd.result {
for path in path_list {
callback(Event::WindowEvent {
@@ -343,10 +341,16 @@ impl EventsLoop {
});
}
}
unsafe {
self.dnd.send_finished(window, source_window, DndState::Accepted)
.expect("Failed to send XDnD finished message.");
}
(source_window, DndState::Accepted)
} else {
// `source_window` won't be part of our DND state if we already rejected the drop in our
// `XdndPosition` handler.
let source_window = client_msg.data.get_long(0) as c_ulong;
(source_window, DndState::Rejected)
};
unsafe {
self.dnd.send_finished(window, source_window, state)
.expect("Failed to send `XdndFinished` message.");
}
self.dnd.reset();
} else if client_msg.message_type == self.dnd.atoms.leave {
@@ -412,10 +416,10 @@ impl EventsLoop {
let new_inner_size = (xev.width as u32, xev.height as u32);
let new_inner_position = (xev.x as i32, xev.y as i32);
let monitor = window.get_current_monitor(); // This must be done *before* locking!
let mut monitor = window.get_current_monitor(); // This must be done *before* locking!
let mut shared_state_lock = window.shared_state.lock();
let (resized, moved) = {
let (mut resized, moved) = {
let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size);
let moved = if is_synthetic {
util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position)
@@ -435,32 +439,8 @@ impl EventsLoop {
(resized, moved)
};
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
// doesn't need this, but Xfwm does.
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
if new_inner_size == rounded_size {
// When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None;
} else {
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
xwindow,
rounded_size.0 as c_uint,
rounded_size.1 as c_uint,
);
}
}
}
let mut events = Events::default();
if resized {
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size));
}
let new_outer_position = if moved || shared_state_lock.position.is_none() {
// We need to convert client area position to window position.
let frame_extents = shared_state_lock.frame_extents
@@ -497,9 +477,9 @@ impl EventsLoop {
});
let new_hidpi_factor = {
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
let monitor = self.xconn.get_monitor_for_window(Some(window_rect));
monitor = self.xconn.get_monitor_for_window(Some(window_rect));
let new_hidpi_factor = monitor.hidpi_factor;
shared_state_lock.last_monitor = Some(monitor);
shared_state_lock.last_monitor = Some(monitor.clone());
new_hidpi_factor
};
if last_hidpi_factor != new_hidpi_factor {
@@ -512,23 +492,50 @@ impl EventsLoop {
);
flusher.queue();
shared_state_lock.dpi_adjusted = Some((new_width, new_height));
// if the DPI factor changed, force a resize event to ensure the logical
// size is computed with the right DPI factor
resized = true;
}
}
// This is a hack to ensure that the DPI adjusted resize is actually applied on all WMs. KWin
// doesn't need this, but Xfwm does.
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
if new_inner_size == rounded_size {
// When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None;
} else {
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
xwindow,
rounded_size.0 as c_uint,
rounded_size.1 as c_uint,
);
}
}
}
if resized {
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size));
}
events
});
if let Some(events) = events {
let window_id = mkwid(xwindow);
if let Some(event) = events.dpi_changed {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.resized {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.moved {
callback(Event::WindowEvent { window_id, event });
}
if let Some(event) = events.dpi_changed {
callback(Event::WindowEvent { window_id, event });
}
}
}

View File

@@ -22,7 +22,7 @@ impl From<bool> for StateOperation {
}
/// X window type. Maps directly to
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html).
/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum WindowType {
/// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
@@ -41,6 +41,24 @@ pub enum WindowType {
Splash,
/// This is a dialog window.
Dialog,
/// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
/// This property is typically used on override-redirect windows.
DropdownMenu,
/// A popup menu that usually appears when the user right clicks on an object.
/// This property is typically used on override-redirect windows.
PopupMenu,
/// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
/// This property is typically used on override-redirect windows.
Tooltip,
/// The window is a notification.
/// This property is typically used on override-redirect windows.
Notification,
/// This should be used on the windows that are popped up by combo boxes.
/// This property is typically used on override-redirect windows.
Combo,
/// This indicates the the window is being dragged.
/// This property is typically used on override-redirect windows.
Dnd,
/// This is a normal, top-level window.
Normal,
}
@@ -62,6 +80,12 @@ impl WindowType {
&Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0",
&Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0",
&Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
&DropdownMenu => b"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0",
&PopupMenu => b"_NET_WM_WINDOW_TYPE_POPUP_MENU\0",
&Tooltip => b"_NET_WM_WINDOW_TYPE_TOOLTIP\0",
&Notification => b"_NET_WM_WINDOW_TYPE_NOTIFICATION\0",
&Combo => b"_NET_WM_WINDOW_TYPE_COMBO\0",
&Dnd => b"_NET_WM_WINDOW_TYPE_DND\0",
&Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
};
unsafe { xconn.get_atom_unchecked(atom_name) }

View File

@@ -272,6 +272,10 @@ impl UnownedWindow {
window.set_window_type(pl_attribs.x11_window_type).queue();
}
if let Some(variant) = pl_attribs.gtk_theme_variant {
window.set_gtk_theme_variant(variant).queue();
}
// set size hints
{
let mut min_dimensions = window_attrs.min_dimensions;
@@ -457,6 +461,19 @@ impl UnownedWindow {
)
}
fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher {
let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") };
let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") };
let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte");
self.xconn.change_property(
self.xwindow,
hint_atom,
utf8_atom,
util::PropMode::Replace,
variant.as_bytes(),
)
}
#[inline]
pub fn set_urgent(&self, is_urgent: bool) {
let mut wm_hints = self.xconn.get_wm_hints(self.xwindow).expect("`XGetWMHints` failed");
@@ -583,7 +600,7 @@ impl UnownedWindow {
wm_name_atom,
utf8_atom,
util::PropMode::Replace,
title.as_bytes_with_nul(),
title.as_bytes(),
)
}
}

View File

@@ -315,6 +315,27 @@ impl EventsLoop {
});
match event_type {
// https://github.com/glfw/glfw/blob/50eccd298a2bbc272b4977bd162d3e4b55f15394/src/cocoa_window.m#L881
appkit::NSKeyUp => {
if let Some(key_window) = maybe_key_window() {
if event_mods(ns_event).logo {
let _: () = msg_send![*key_window.window, sendEvent:ns_event];
}
}
None
},
// similar to above, but for `<Cmd-.>`, the keyDown is suppressed instead of the
// KeyUp, and the above trick does not appear to work.
appkit::NSKeyDown => {
let modifiers = event_mods(ns_event);
let keycode = NSEvent::keyCode(ns_event);
if modifiers.logo && keycode == 47 {
modifier_event(ns_event, NSEventModifierFlags::NSCommandKeyMask, false)
.map(into_event)
} else {
None
}
},
appkit::NSFlagsChanged => {
let mut events = std::collections::VecDeque::new();
@@ -589,7 +610,7 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
0x3d => events::VirtualKeyCode::RAlt,
0x3e => events::VirtualKeyCode::RControl,
//0x3f => Fn key,
//0x40 => F17 Key,
0x40 => events::VirtualKeyCode::F17,
0x41 => events::VirtualKeyCode::Decimal,
//0x42 -> unkown,
0x43 => events::VirtualKeyCode::Multiply,
@@ -604,8 +625,8 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
0x4c => events::VirtualKeyCode::NumpadEnter,
//0x4d => unkown,
0x4e => events::VirtualKeyCode::Subtract,
//0x4f => F18 key,
//0x50 => F19 Key,
0x4f => events::VirtualKeyCode::F18,
0x50 => events::VirtualKeyCode::F19,
0x51 => events::VirtualKeyCode::NumpadEquals,
0x52 => events::VirtualKeyCode::Numpad0,
0x53 => events::VirtualKeyCode::Numpad1,
@@ -615,7 +636,7 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
0x57 => events::VirtualKeyCode::Numpad5,
0x58 => events::VirtualKeyCode::Numpad6,
0x59 => events::VirtualKeyCode::Numpad7,
//0x5a => F20 Key,
0x5a => events::VirtualKeyCode::F20,
0x5b => events::VirtualKeyCode::Numpad8,
0x5c => events::VirtualKeyCode::Numpad9,
//0x5d => unkown,
@@ -631,7 +652,7 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
0x67 => events::VirtualKeyCode::F11,
//0x68 => unkown,
0x69 => events::VirtualKeyCode::F13,
//0x6a => F16 Key,
0x6a => events::VirtualKeyCode::F16,
0x6b => events::VirtualKeyCode::F14,
//0x6c => unkown,
0x6d => events::VirtualKeyCode::F10,
@@ -659,6 +680,23 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
})
}
pub fn check_additional_virtual_key_codes(
s: &Option<String>
) -> Option<events::VirtualKeyCode> {
if let &Some(ref s) = s {
if let Some(ch) = s.encode_utf16().next() {
return Some(match ch {
0xf718 => events::VirtualKeyCode::F21,
0xf719 => events::VirtualKeyCode::F22,
0xf71a => events::VirtualKeyCode::F23,
0xf71b => events::VirtualKeyCode::F24,
_ => return None,
})
}
}
None
}
pub fn event_mods(event: cocoa::base::id) -> ModifiersState {
let flags = unsafe {
NSEvent::modifierFlags(event)

View File

@@ -11,10 +11,10 @@ use cocoa::base::{id, nil};
use cocoa::appkit::{NSEvent, NSView, NSWindow};
use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger};
use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Protocol, Sel, BOOL};
use objc::runtime::{Class, Object, Protocol, Sel, BOOL, YES};
use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId};
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code};
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code, check_additional_virtual_key_codes};
use platform::platform::util;
use platform::platform::ffi::*;
use platform::platform::window::{get_window_id, IdRef};
@@ -24,7 +24,7 @@ struct ViewState {
shared: Weak<Shared>,
ime_spot: Option<(f64, f64)>,
raw_characters: Option<String>,
last_insert: Option<String>,
is_key_down: bool,
}
pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef {
@@ -33,7 +33,7 @@ pub fn new_view(window: id, shared: Weak<Shared>) -> IdRef {
shared,
ime_spot: None,
raw_characters: None,
last_insert: None,
is_key_down: false,
};
unsafe {
// This is free'd in `dealloc`
@@ -122,6 +122,7 @@ lazy_static! {
decl.add_method(sel!(mouseDragged:), mouse_dragged as extern fn(&Object, Sel, id));
decl.add_method(sel!(rightMouseDragged:), right_mouse_dragged as extern fn(&Object, Sel, id));
decl.add_method(sel!(otherMouseDragged:), other_mouse_dragged as extern fn(&Object, Sel, id));
decl.add_method(sel!(_wantsKeyDownForEvent:), wants_key_down_for_event as extern fn(&Object, Sel, id) -> BOOL);
decl.add_ivar::<*mut c_void>("winitState");
decl.add_ivar::<id>("markedText");
let protocol = Protocol::get("NSTextInputClient").unwrap();
@@ -279,7 +280,7 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
characters.len(),
);
let string = str::from_utf8_unchecked(slice);
state.last_insert = Some(string.to_owned());
state.is_key_down = true;
// We don't need this now, but it's here if that changes.
//let event: id = msg_send![class!(NSApp), currentEvent];
@@ -343,6 +344,18 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
}
}
fn get_characters(event: id) -> Option<String> {
unsafe {
let characters: id = msg_send![event, characters];
let slice = slice::from_raw_parts(
characters.UTF8String() as *const c_uchar,
characters.len(),
);
let string = str::from_utf8_unchecked(slice);
Some(string.to_owned())
}
}
extern fn key_down(this: &Object, _sel: Sel, event: id) {
//println!("keyDown");
unsafe {
@@ -350,8 +363,16 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
let state = &mut *(state_ptr as *mut ViewState);
let window_id = WindowId(get_window_id(state.window));
state.raw_characters = get_characters(event);
let keycode: c_ushort = msg_send![event, keyCode];
let virtual_keycode = to_virtual_key_code(keycode);
// We are checking here for F21-F24 keys, since their keycode
// can vary, but we know that they are encoded
// in characters property.
let virtual_keycode = to_virtual_key_code(keycode)
.or_else(|| {
check_additional_virtual_key_codes(&state.raw_characters)
});
let scancode = keycode as u32;
let is_repeat = msg_send![event, isARepeat];
@@ -368,13 +389,14 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
},
};
state.raw_characters = {
let characters: id = msg_send![event, characters];
let slice = slice::from_raw_parts(
characters.UTF8String() as *const c_uchar,
characters.len(),
let characters: id = msg_send![event, characters];
let slice = slice::from_raw_parts(
characters.UTF8String() as *const c_uchar,
characters.len(),
);
let string = str::from_utf8_unchecked(slice);
let string = str::from_utf8_unchecked(slice);
state.raw_characters = {
Some(string.to_owned())
};
@@ -384,9 +406,8 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
.unwrap()
.push_back(window_event);
// Emit `ReceivedCharacter` for key repeats
if is_repeat && state.last_insert.is_some() {
let last_insert = state.last_insert.as_ref().unwrap();
for character in last_insert.chars() {
if is_repeat && state.is_key_down{
for character in string.chars() {
let window_event = Event::WindowEvent {
window_id,
event: WindowEvent::ReceivedCharacter(character),
@@ -413,10 +434,17 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
state.last_insert = None;
state.is_key_down = false;
// We need characters here to check for additional keys such as
// F21-F24.
let characters = get_characters(event);
let keycode: c_ushort = msg_send![event, keyCode];
let virtual_keycode = to_virtual_key_code(keycode);
let virtual_keycode = to_virtual_key_code(keycode)
.or_else(|| {
check_additional_virtual_key_codes(&characters)
});
let scancode = keycode as u32;
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.window)),
@@ -566,3 +594,8 @@ extern fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
extern fn wants_key_down_for_event(_this: &Object, _se: Sel, _event: id) -> BOOL {
YES
}

View File

@@ -1,21 +1,24 @@
use std;
use std::cell::{Cell, RefCell};
use std::f64;
use std::ops::Deref;
use std::os::raw::c_void;
use std::sync::Weak;
use std::sync::atomic::{Ordering, AtomicBool};
use cocoa;
use cocoa::appkit::{
self,
CGFloat,
NSApp,
NSApplication,
NSColor,
NSRequestUserAttentionType,
NSScreen,
NSView,
NSWindow,
NSWindowButton,
NSWindowStyleMask,
NSApplicationActivationPolicy,
};
use cocoa::base::{id, nil};
use cocoa::foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize, NSString};
@@ -584,6 +587,19 @@ impl WindowExt for Window2 {
fn get_nsview(&self) -> *mut c_void {
*self.view as *mut c_void
}
#[inline]
fn request_user_attention(&self, is_critical: bool) {
let request_type = if is_critical {
NSRequestUserAttentionType::NSCriticalRequest
} else {
NSRequestUserAttentionType::NSInformationalRequest
};
unsafe {
NSApp().requestUserAttention_(request_type);
}
}
}
impl Window2 {
@@ -718,7 +734,15 @@ impl Window2 {
if app == nil {
None
} else {
app.setActivationPolicy_(activation_policy.into());
let ns_activation_policy = match activation_policy {
ActivationPolicy::Regular =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
ActivationPolicy::Accessory =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory,
ActivationPolicy::Prohibited =>
NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited,
};
app.setActivationPolicy_(ns_activation_policy);
app.finishLaunching();
Some(app)
}
@@ -851,6 +875,16 @@ impl Window2 {
let view = new_view(window, shared);
view.non_nil().map(|view| {
view.setWantsBestResolutionOpenGLSurface_(YES);
// On Mojave, views automatically become layer-backed shortly after being added to
// a window. Changing the layer-backedness of a view breaks the association between
// the view and its associated OpenGL context. To work around this, on Mojave we
// explicitly make the view layer-backed up front so that AppKit doesn't do it
// itself and break the association with its context.
if f64::floor(appkit::NSAppKitVersionNumber) > appkit::NSAppKitVersionNumber10_12 {
view.setWantsLayer(YES);
}
window.setContentView_(*view);
window.makeFirstResponder_(*view);
view

View File

@@ -0,0 +1,203 @@
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{mem, ptr};
use winapi::ctypes::c_void;
use winapi::shared::guiddef::REFIID;
use winapi::shared::minwindef::{DWORD, MAX_PATH, UINT, ULONG};
use winapi::shared::windef::{HWND, POINTL};
use winapi::shared::winerror::S_OK;
use winapi::um::objidl::IDataObject;
use winapi::um::oleidl::{IDropTarget, IDropTargetVtbl};
use winapi::um::winnt::HRESULT;
use winapi::um::{shellapi, unknwnbase};
use platform::platform::events_loop::send_event;
use platform::platform::WindowId;
use {Event, WindowId as SuperWindowId};
#[repr(C)]
pub struct FileDropHandlerData {
pub interface: IDropTarget,
refcount: AtomicUsize,
window: HWND,
}
pub struct FileDropHandler {
pub data: *mut FileDropHandlerData,
}
#[allow(non_snake_case)]
impl FileDropHandler {
pub fn new(window: HWND) -> FileDropHandler {
let data = Box::new(FileDropHandlerData {
interface: IDropTarget {
lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl,
},
refcount: AtomicUsize::new(1),
window,
});
FileDropHandler {
data: Box::into_raw(data),
}
}
// Implement IUnknown
pub unsafe extern "system" fn QueryInterface(
_this: *mut unknwnbase::IUnknown,
_riid: REFIID,
_ppvObject: *mut *mut c_void,
) -> HRESULT {
// This function doesn't appear to be required for an `IDropTarget`.
// An implementation would be nice however.
unimplemented!();
}
pub unsafe extern "system" fn AddRef(this: *mut unknwnbase::IUnknown) -> ULONG {
let drop_handler_data = Self::from_interface(this);
let count = drop_handler_data.refcount.fetch_add(1, Ordering::Release) + 1;
count as ULONG
}
pub unsafe extern "system" fn Release(this: *mut unknwnbase::IUnknown) -> ULONG {
let drop_handler = Self::from_interface(this);
let count = drop_handler.refcount.fetch_sub(1, Ordering::Release) - 1;
if count == 0 {
// Destroy the underlying data
Box::from_raw(drop_handler as *mut FileDropHandlerData);
}
count as ULONG
}
pub unsafe extern "system" fn DragEnter(
this: *mut IDropTarget,
pDataObj: *const IDataObject,
_grfKeyState: DWORD,
_pt: *const POINTL,
_pdwEffect: *mut DWORD,
) -> HRESULT {
use events::WindowEvent::HoveredFile;
let drop_handler = Self::from_interface(this);
Self::iterate_filenames(pDataObj, |filename| {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(drop_handler.window)),
event: HoveredFile(filename),
});
});
S_OK
}
pub unsafe extern "system" fn DragOver(
_this: *mut IDropTarget,
_grfKeyState: DWORD,
_pt: *const POINTL,
_pdwEffect: *mut DWORD,
) -> HRESULT {
S_OK
}
pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT {
use events::WindowEvent::HoveredFileCancelled;
let drop_handler = Self::from_interface(this);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(drop_handler.window)),
event: HoveredFileCancelled,
});
S_OK
}
pub unsafe extern "system" fn Drop(
this: *mut IDropTarget,
pDataObj: *const IDataObject,
_grfKeyState: DWORD,
_pt: *const POINTL,
_pdwEffect: *mut DWORD,
) -> HRESULT {
use events::WindowEvent::DroppedFile;
let drop_handler = Self::from_interface(this);
let hdrop = Self::iterate_filenames(pDataObj, |filename| {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(drop_handler.window)),
event: DroppedFile(filename),
});
});
shellapi::DragFinish(hdrop);
S_OK
}
unsafe fn from_interface<'a, InterfaceT>(this: *mut InterfaceT) -> &'a mut FileDropHandlerData {
&mut *(this as *mut _)
}
unsafe fn iterate_filenames<F>(data_obj: *const IDataObject, callback: F) -> shellapi::HDROP
where
F: Fn(PathBuf),
{
use winapi::ctypes::wchar_t;
use winapi::shared::winerror::SUCCEEDED;
use winapi::shared::wtypes::{CLIPFORMAT, DVASPECT_CONTENT};
use winapi::um::objidl::{FORMATETC, TYMED_HGLOBAL};
use winapi::um::shellapi::DragQueryFileW;
use winapi::um::winuser::CF_HDROP;
let mut drop_format = FORMATETC {
cfFormat: CF_HDROP as CLIPFORMAT,
ptd: ptr::null(),
dwAspect: DVASPECT_CONTENT,
lindex: -1,
tymed: TYMED_HGLOBAL,
};
let mut medium = mem::uninitialized();
if SUCCEEDED((*data_obj).GetData(&mut drop_format, &mut medium)) {
let hglobal = (*medium.u).hGlobal();
let hdrop = (*hglobal) as shellapi::HDROP;
// The second parameter (0xFFFFFFFF) instructs the function to return the item count
let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
let mut pathbuf: [wchar_t; MAX_PATH] = mem::uninitialized();
for i in 0..item_count {
let character_count =
DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(), MAX_PATH as UINT) as usize;
if character_count > 0 {
callback(OsString::from_wide(&pathbuf[0..character_count]).into());
}
}
return hdrop;
}
// The call to `GetData` must succeed and the file handle must be returned before this
// point
unreachable!();
}
}
impl Drop for FileDropHandler {
fn drop(&mut self) {
unsafe {
FileDropHandler::Release(self.data as *mut unknwnbase::IUnknown);
}
}
}
static DROP_TARGET_VTBL: IDropTargetVtbl = IDropTargetVtbl {
parent: unknwnbase::IUnknownVtbl {
QueryInterface: FileDropHandler::QueryInterface,
AddRef: FileDropHandler::AddRef,
Release: FileDropHandler::Release,
},
DragEnter: FileDropHandler::DragEnter,
DragOver: FileDropHandler::DragOver,
DragLeave: FileDropHandler::DragLeave,
Drop: FileDropHandler::Drop,
};

View File

@@ -148,7 +148,7 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
winuser::VK_F13 => Some(VirtualKeyCode::F13),
winuser::VK_F14 => Some(VirtualKeyCode::F14),
winuser::VK_F15 => Some(VirtualKeyCode::F15),
/*winuser::VK_F16 => Some(VirtualKeyCode::F16),
winuser::VK_F16 => Some(VirtualKeyCode::F16),
winuser::VK_F17 => Some(VirtualKeyCode::F17),
winuser::VK_F18 => Some(VirtualKeyCode::F18),
winuser::VK_F19 => Some(VirtualKeyCode::F19),
@@ -156,7 +156,7 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
winuser::VK_F21 => Some(VirtualKeyCode::F21),
winuser::VK_F22 => Some(VirtualKeyCode::F22),
winuser::VK_F23 => Some(VirtualKeyCode::F23),
winuser::VK_F24 => Some(VirtualKeyCode::F24),*/
winuser::VK_F24 => Some(VirtualKeyCode::F24),
winuser::VK_NUMLOCK => Some(VirtualKeyCode::Numlock),
winuser::VK_SCROLL => Some(VirtualKeyCode::Scroll),
winuser::VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward),

View File

@@ -15,10 +15,8 @@
use std::{mem, ptr, thread};
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::os::windows::io::AsRawHandle;
use std::sync::{Arc, Barrier, Condvar, mpsc, Mutex};
use std::sync::{Arc, Barrier, mpsc, Mutex};
use winapi::ctypes::c_int;
use winapi::shared::minwindef::{
@@ -29,13 +27,14 @@ use winapi::shared::minwindef::{
LOWORD,
LPARAM,
LRESULT,
MAX_PATH,
UINT,
WPARAM,
};
use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::shared::windowsx;
use winapi::um::{winuser, shellapi, processthreadsapi};
use winapi::shared::winerror::S_OK;
use winapi::um::{winuser, processthreadsapi, ole2};
use winapi::um::oleidl::LPDROPTARGET;
use winapi::um::winnt::{LONG, LPCSTR, SHORT};
use {
@@ -57,6 +56,7 @@ use platform::platform::dpi::{
enable_non_client_dpi_scaling,
get_hwnd_scale_factor,
};
use platform::platform::drop_handler::FileDropHandler;
use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey};
use platform::platform::icon::WinIcon;
use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state};
@@ -138,10 +138,6 @@ pub struct EventsLoop {
thread_id: DWORD,
// Receiver for the events. The sender is in the background thread.
receiver: mpsc::Receiver<Event>,
// Variable that contains the block state of the win32 event loop thread during a WM_SIZE event.
// The mutex's value is `true` when it's blocked, and should be set to false when it's done
// blocking. That's done by the parent thread when it receives a Resized event.
win32_block_loop: Arc<(Mutex<bool>, Condvar)>,
}
impl EventsLoop {
@@ -154,8 +150,6 @@ impl EventsLoop {
// The main events transfer channel.
let (tx, rx) = mpsc::channel();
let win32_block_loop = Arc::new((Mutex::new(false), Condvar::new()));
let win32_block_loop_child = win32_block_loop.clone();
// Local barrier in order to block the `new()` function until the background thread has
// an events queue.
@@ -167,8 +161,8 @@ impl EventsLoop {
*context_stash.borrow_mut() = Some(ThreadLocalData {
sender: tx,
windows: HashMap::with_capacity(4),
win32_block_loop: win32_block_loop_child,
mouse_buttons_down: 0
file_drop_handlers: HashMap::with_capacity(4),
mouse_buttons_down: 0,
});
});
@@ -220,7 +214,6 @@ impl EventsLoop {
EventsLoop {
thread_id,
receiver: rx,
win32_block_loop,
}
}
@@ -232,18 +225,8 @@ impl EventsLoop {
Ok(e) => e,
Err(_) => return
};
let is_resize = match event {
Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true,
_ => false
};
callback(event);
if is_resize {
let (ref mutex, ref cvar) = *self.win32_block_loop;
let mut block_thread = mutex.lock().unwrap();
*block_thread = false;
cvar.notify_all();
}
}
}
@@ -255,18 +238,8 @@ impl EventsLoop {
Ok(e) => e,
Err(_) => return
};
let is_resize = match event {
Event::WindowEvent{ event: WindowEvent::Resized(..), .. } => true,
_ => false
};
let flow = callback(event);
if is_resize {
let (ref mutex, ref cvar) = *self.win32_block_loop;
let mut block_thread = mutex.lock().unwrap();
*block_thread = false;
cvar.notify_all();
}
match flow {
ControlFlow::Continue => continue,
ControlFlow::Break => break,
@@ -399,12 +372,12 @@ thread_local!(static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::
struct ThreadLocalData {
sender: mpsc::Sender<Event>,
windows: HashMap<HWND, Arc<Mutex<WindowState>>>,
win32_block_loop: Arc<(Mutex<bool>, Condvar)>,
mouse_buttons_down: u32
file_drop_handlers: HashMap<HWND, FileDropHandler>, // Each window has its own drop handler.
mouse_buttons_down: u32,
}
// Utility function that dispatches an event on the current thread.
fn send_event(event: Event) {
pub fn send_event(event: Event) {
CONTEXT_STASH.with(|context_stash| {
let context_stash = context_stash.borrow();
@@ -452,6 +425,30 @@ pub unsafe extern "system" fn callback(
lparam: LPARAM,
) -> LRESULT {
match msg {
winuser::WM_CREATE => {
use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE};
let ole_init_result = ole2::OleInitialize(ptr::null_mut());
// It is ok if the initialize result is `S_FALSE` because it might happen that
// multiple windows are created on the same thread.
if ole_init_result == OLE_E_WRONGCOMPOBJ {
panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`");
} else if ole_init_result == RPC_E_CHANGED_MODE {
panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`");
}
CONTEXT_STASH.with(|context_stash| {
let mut context_stash = context_stash.borrow_mut();
let drop_handlers = &mut context_stash.as_mut().unwrap().file_drop_handlers;
let new_handler = FileDropHandler::new(window);
let handler_interface_ptr = &mut (*new_handler.data).interface as LPDROPTARGET;
drop_handlers.insert(window, new_handler);
assert_eq!(ole2::RegisterDragDrop(window, handler_interface_ptr), S_OK);
});
0
},
winuser::WM_NCCREATE => {
enable_non_client_dpi_scaling(window);
winuser::DefWindowProcW(window, msg, wparam, lparam)
@@ -470,7 +467,10 @@ pub unsafe extern "system" fn callback(
use events::WindowEvent::Destroyed;
CONTEXT_STASH.with(|context_stash| {
let mut context_stash = context_stash.borrow_mut();
context_stash.as_mut().unwrap().windows.remove(&window);
ole2::RevokeDragDrop(window);
let context_stash_mut = context_stash.as_mut().unwrap();
context_stash_mut.file_drop_handlers.remove(&window);
context_stash_mut.windows.remove(&window);
});
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
@@ -527,24 +527,7 @@ pub unsafe extern "system" fn callback(
event: Resized(logical_size),
};
// If this window has been inserted into the window map, the resize event happened
// during the event loop. If it hasn't, the event happened on window creation and
// should be ignored.
if cstash.windows.get(&window).is_some() {
let (ref mutex, ref cvar) = *cstash.win32_block_loop;
let mut block_thread = mutex.lock().unwrap();
*block_thread = true;
// The event needs to be sent after the lock to ensure that `notify_all` is
// called after `wait`.
cstash.sender.send(event).ok();
while *block_thread {
block_thread = cvar.wait(block_thread).unwrap();
}
} else {
cstash.sender.send(event).ok();
}
cstash.sender.send(event).ok();
});
0
},
@@ -1039,24 +1022,7 @@ pub unsafe extern "system" fn callback(
},
winuser::WM_DROPFILES => {
use events::WindowEvent::DroppedFile;
let hdrop = wparam as shellapi::HDROP;
let mut pathbuf: [u16; MAX_PATH] = mem::uninitialized();
let num_drops = shellapi::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
for i in 0..num_drops {
let nch = shellapi::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
MAX_PATH as u32) as usize;
if nch > 0 {
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into())
});
}
}
shellapi::DragFinish(hdrop);
// See `FileDropHandler` for implementation.
0
},

View File

@@ -18,7 +18,7 @@ unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
// Cursor name in UTF-16. Used to set cursor in `WM_SETCURSOR`.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct Cursor(pub *const winapi::ctypes::wchar_t);
unsafe impl Send for Cursor {}
unsafe impl Sync for Cursor {}
@@ -49,6 +49,7 @@ unsafe impl Send for WindowId {}
unsafe impl Sync for WindowId {}
mod dpi;
mod drop_handler;
mod event;
mod events_loop;
mod icon;

View File

@@ -13,6 +13,7 @@ use winapi::shared::windef::{HWND, LPPOINT, POINT, RECT};
use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser};
use winapi::um::objbase::COINIT_MULTITHREADED;
use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2};
use winapi::um::wingdi::{CreateRectRgn, DeleteObject};
use winapi::um::winnt::{LONG, LPCWSTR};
use {
@@ -301,7 +302,7 @@ impl Window {
#[inline]
pub fn set_cursor(&self, cursor: MouseCursor) {
let cursor_id = match cursor {
let cursor_id = Cursor(match cursor {
MouseCursor::Arrow | MouseCursor::Default => winuser::IDC_ARROW,
MouseCursor::Hand => winuser::IDC_HAND,
MouseCursor::Crosshair => winuser::IDC_CROSS,
@@ -321,10 +322,15 @@ impl Window {
MouseCursor::Progress => winuser::IDC_APPSTARTING,
MouseCursor::Help => winuser::IDC_HELP,
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
};
let mut cur = self.window_state.lock().unwrap();
cur.cursor = Cursor(cursor_id);
});
self.window_state.lock().unwrap().cursor = cursor_id;
self.events_loop_proxy.execute_in_thread(move |_| unsafe {
let cursor = winuser::LoadCursorW(
ptr::null_mut(),
cursor_id.0,
);
winuser::SetCursor(cursor);
});
}
unsafe fn cursor_is_grabbed(&self) -> Result<bool, String> {
@@ -448,25 +454,24 @@ impl Window {
#[inline]
pub fn set_maximized(&self, maximized: bool) {
let mut window_state = self.window_state.lock().unwrap();
window_state.maximized = true;
// We only maximize if we're not in fullscreen.
if window_state.fullscreen.is_none() {
return;
}
let window = self.window.clone();
unsafe {
// `ShowWindow` resizes the window, so it must be called from the main thread.
self.events_loop_proxy.execute_in_thread(move |_| {
winuser::ShowWindow(
window.0,
if maximized {
winuser::SW_MAXIMIZE
} else {
winuser::SW_RESTORE
},
);
});
if mem::replace(&mut window_state.maximized, maximized) != maximized {
// We only maximize if we're not in fullscreen.
if window_state.fullscreen.is_none() {
let window = self.window.clone();
unsafe {
// `ShowWindow` resizes the window, so it must be called from the main thread.
self.events_loop_proxy.execute_in_thread(move |_| {
winuser::ShowWindow(
window.0,
if maximized {
winuser::SW_MAXIMIZE
} else {
winuser::SW_RESTORE
},
);
});
}
}
}
}
@@ -899,6 +904,9 @@ unsafe fn init(
if pl_attribs.no_redirection_bitmap {
ex_style |= winuser::WS_EX_NOREDIRECTIONBITMAP;
}
if attributes.transparent && attributes.decorations {
ex_style |= winuser::WS_EX_LAYERED;
}
// adjusting the window coordinates using the style
winuser::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
@@ -1013,14 +1021,29 @@ unsafe fn init(
// 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: 0x1, // FIXME: DWM_BB_ENABLE;
dwFlags: dwmapi::DWM_BB_ENABLE | dwmapi::DWM_BB_BLURREGION,
fEnable: 1,
hRgnBlur: ptr::null_mut(),
hRgnBlur: region,
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;
// 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);
}
}
let win = Window {

39
tests/serde_objects.rs Normal file
View File

@@ -0,0 +1,39 @@
#![cfg(feature = "serde")]
extern crate serde;
extern crate winit;
use winit::{ControlFlow, MouseCursor};
use winit::{
KeyboardInput, TouchPhase, ElementState, MouseButton, MouseScrollDelta, VirtualKeyCode,
ModifiersState
};
use winit::dpi::{LogicalPosition, PhysicalPosition, LogicalSize, PhysicalSize};
use serde::{Serialize, Deserialize};
fn needs_serde<S: Serialize + Deserialize<'static>>() {}
#[test]
fn root_serde() {
needs_serde::<ControlFlow>();
needs_serde::<MouseCursor>();
}
#[test]
fn events_serde() {
needs_serde::<KeyboardInput>();
needs_serde::<TouchPhase>();
needs_serde::<ElementState>();
needs_serde::<MouseButton>();
needs_serde::<MouseScrollDelta>();
needs_serde::<VirtualKeyCode>();
needs_serde::<ModifiersState>();
}
#[test]
fn dpi_serde() {
needs_serde::<LogicalPosition>();
needs_serde::<PhysicalPosition>();
needs_serde::<LogicalSize>();
needs_serde::<PhysicalSize>();
}