Compare commits

...

35 Commits
it ... v0.19.3

Author SHA1 Message Date
Alex Touchet
341fe47c56 Update parking_lot and bump version (#1109) 2019-08-26 18:02:43 -04:00
Osspial
8119a7df13 Bump version to 0.19.2 2019-07-29 16:17:34 -04:00
Simon Sapin
5c02c20b05 Update the percent-encoding crate to 2.0 (#1076) 2019-07-29 16:14:26 -04:00
Osspial
744913d482 Update README.md
For the last time on this branch, it seems. Godspeed.
2019-06-13 01:43:13 -04:00
aloucks
7dc48ed9ef Direct new contributors to the eventloop-2.0 branch (#903) 2019-06-10 01:06:55 -06:00
Osspial
30c0058a88 Update CONTRIBUTING.md (#897) 2019-06-03 16:38:15 -04:00
Bastien Orivel
8794157370 Update parking_lot to 0.8 (#881) 2019-05-25 10:05:47 -06:00
Lucas Kent
1a92c46ad7 Fix warnings on linux (#879) 2019-05-24 05:10:49 -06:00
Alex Touchet
22288ec4c1 Update URLs (#863) 2019-05-11 07:48:33 -06:00
Felix Rabe
ffa6815321 README: Link to FEATURES.md and missing features wiki page (#860)
Closes #854
2019-05-09 02:45:28 -04:00
Felix Rabe
8ddc7fdaf6 README: Use shields.io instead of Herokuapp (#859) 2019-05-09 02:44:55 -04:00
Jakub Piecuch
594cd18567 Fix monitor width & height sanity check (#861)
Fix monitor width & height sanity check
2019-05-05 14:54:57 -06:00
acheronfail
4469f29e70 Feat/fullscreen getters (#838)
* feat: [macos] add get_fullscreen and get_simple_fullscreen

* feat: [windows] add get_fullscreen

* feat: [ios] add get_fullscreen

* feat: [android] add get_fullscreen

* feat: [emscripten] add get_fullscreen

* feat: [linux] add get_fullscreen

* feedback: `get_fullscreen() -> bool` -> `get_fullscreen() -> Option<Id>`
2019-04-25 13:09:32 -04:00
Osspial
4e5321b0aa Fix CI links in README.md (#852) 2019-04-25 01:20:54 -04:00
Felix Rabe
4b2e9da4e4 Popup windows are also known as modal windows (#848) 2019-04-24 22:56:40 -04:00
Osspial
b94572621a Makes changes to CONTRIBUTING.md's table as discussed in #830 (#841)
This includes de-listing @francesca64 from the table, as I suggested. I
realize that this is a controversial decision, and it's not a decision I
make lightly, but I believe I have justification for doing so:

I contacted @francesca64 last month asking her about her inactivity as a
maintainer on this project. She replied on March 26th as follows. For the
sake of her privacy, I've removed removed certain sections of her response,
as it contains some personal details that I'm not comfortable sharing with
the world at large without her explicit permission.

> Hello! Thanks for reaching out!❤

> In short, I've moved on. <removed> ...in November, I was hired by a
> company that recently started using Rust. I'm very happily building
> infrastructure there!

> I'm simply not interested in spending more than 8 hours a day
> programming. You couldn't even pay me to do it! My time is best spent
> going on adventures with my beloved.

> I'll still be active in the Rust ecosystem, but only insofar as the
> company I work for is. We use winit on iOS, Android, and (to a limited
> extent) macOS, so I'll work on those backends as needed.

> Thank you for taking care of winit. I hope you're taking care of
> yourself too; <removed>.

I don't begrudge her for her decision, and others shouldn't either - I
firmly believe that, as this is unpaid, volunteer work, everybody should
have the right to move on when they decide they no longer have the time
or will to contribute.

The exact impliciation of this in regards to her status as maintainer
is open to interpretation. I would argue that this means, should she in
her work stumble upon an issue in any of her listed backends, she would be
willing to submit PRs addressing those issues. However, it also means
that she is not able to put in the time to be active as a maintainer on
those platforms, or review PRs and issues for those platforms.

On March 28th I responded to her email as follows:

> Hey, thanks for responding.

> <removed, personal details>

> In the meanwhile, there are still a few loose ends from your time as
> maintainer that I'd like to get cleaned up. It's okay if you aren't
> going to be spending much time on Winit, but there's still a broad
> assumption that you're able to review PRs for them and that doesn't seem
> to be the case. Would you be able to do a couple things to ease the
> transition to whoever next takes over the macOS, X11, and Android
> backends?

> 1) Submit a PR downgrading yourself from maintainer for macOS, X11, and
>    Android, so somebody else can more actively take them over.
> 2) Post your WIP macOS backend for EL2.0 as well as the issues it
>    currently has, so whoever next maintains macOS can finish it.

> Going forward, I'd like to reach out to the broader Rust community and
> find more active maintainers so Winit can get to 1.0 and I can mostly
> move on from it.

I recieved no response to that email. On April 4th, I followed up on
that email:

> If you aren't able to act as a maintainer for Winit, and aren't able to
> submit a PR updating your official status as maintainer to reflect
> reality, would you mind if I submitted a PR removing you as maintainer?
> That's not something I want to do since it's a bad image for me, a bad
> image for Winit, and sets an extremely uncomfortable precedent, but I'd
> like to start more aggressive outreach to ensure each backend is less
> dependent on one specific person and I don't want to see new
> contributors pinging you for help when you're unable to provide it.

> If you don't reply by the 11th that's the path I'm going to take, but I
> consider it the nuclear option and I want to avoid invoking it if at all
> possible.

Up to this date (April 13th), I have recieved no response. Given the
amount of time I've given her to respond, as well as her lack of
response, I believe we have the justification to remove her from the
table. Should she show back up again, any clarifications on her status
would be welcome, and she is welcome to submit a PR re-listing herself
on the table with a more accurate description of her current contributor
status. However, once we begin the contributor marketing push discussed
in #830, I don't want new contributors to attempt to ask her questions
on the macOS, X11, or Android backends when she isn't able to give a
response.

-----------------------------------------------------------------------

This PR also introduces HALL_OF_CHAMPIONS.md, which commends the efforts
of former maintainers that have contributed greatly to the Winit project.
This wasn't discussed previously, but I think it's important to recognize
the people that brought us to where we are today. It currently lists
@tomaka and @francesca64, as they are the two individuals I'm aware of
that both deserve such recognition and no longer actively contribute to
Winit, but if there's anybody I missed feel free to suggest them and a
blurb describing their work.
2019-04-24 12:44:21 +02:00
Osspial
7203ec45b5 Fix TODO in CONTRIBUTING.md 2019-04-14 12:05:37 -04:00
Osspial
fc835f383b Fix link in PULL_REQUEST_TEMPLATE.md 2019-04-13 20:42:22 -04:00
Osspial
2f9607694f Winit Features and Scope (#695)
* Add initial draft of SCOPE document

* Rephrase/rename feature tiers

* Rename to FEATURES and add a few annotations

* Fix API Reworks table

* Add more annotations

* Some phrasing

* Split compat matrix into seperate section, to be moved into wiki

* Mention compatibility in CONTRIBUTING

* Remove some discuss annotations

* Apply review changes and rename child windows feature to popup windows

* Update based on discussion

* Add issue for Android HiDPI

* Update FEATURES.md

* Update FEATURES.md

* Update PULL_REQUEST_TEMPLATE.md

* Update PULL_REQUEST_TEMPLATE.md

* Reformat FEATURES.MD

* Remove comments

* Improve formatting and add guide for extending #Features
2019-04-13 18:57:08 -04:00
Osspial
cd5caf6a22 Update for 0.19.1 (#823) 2019-04-08 01:08:31 -04:00
Hal Gentz
8522071c2c Add ability to get wayland display from events loop. (#829)
Signed-off-by: Hal Gentz <zegentzy@protonmail.com>
2019-04-08 01:07:47 -04:00
Osspial
dfa972eab1 Fix window icon (#831)
* Fix window icon

* Add CHANGELOG entry
2019-04-08 01:07:12 -04:00
mitchmindtree
69585fe2f2 [Rebased] [x11-backend] Retrieve DPI from Xft.dpi XResource (#824)
* [x11-backend] Retrieve DPI from Xft.dpi XResource

* Update CHANGELOG.md

* Update window.rs

* Update CHANGELOG.md
2019-04-07 12:48:21 -04:00
Christian Duerr
c0b2cad3f9 Add additional numpad key mappings (#805)
* Add additional numpad key mappings

Since some platforms have already used the existing `Add`, `Subtract`
and `Divide` codes to map numpad keys, the X11 and Wayland platform has
been updated to achieve parity between platforms. On macOS only the
`Subtract` numpad key had to be added.

Since the numpad key is different from the normal keys, an alternative
option would be to add new `NumpadAdd`, `NumpadSubtract` and
`NumpadDivide` actions, however I think in this case it should be fine
to map them to the same virtual key code.

* Add Numpad PageUp/Down, Home and End on Wayland
2019-04-07 01:25:37 -04:00
TakWolf
57680d2d17 fix command key event left and right reverse on macOS (#810)
* fix command key event left and right reverse on macOS

https://github.com/tomaka/winit/issues/808

* update changelog
2019-03-25 14:05:07 -04:00
Tobias Kortkamp
0019ff210c Fix build on FreeBSD (#815)
* Fix build on FreeBSD

error[E0432]: unresolved import `libc::__errno_location`
  --> src/platform/linux/x11/mod.rs:22:85
   |
22 | use libc::{select, fd_set, FD_SET, FD_ZERO, FD_ISSET, EINTR, EINVAL, ENOMEM, EBADF, __errno_location};
   |                                                                                     ^^^^^^^^^^^^^^^^ no `__errno_location` in the root

__errno_location is called __error on FreeBSD and __errno on Open- and NetBSD.

Signed-off-by: Tobias Kortkamp <t@tobik.me>

* Import __error / __errno on *BSD as __errno_location

Signed-off-by: Tobias Kortkamp <t@tobik.me>

* Add changelog entry

Signed-off-by: Tobias Kortkamp <t@tobik.me>
2019-03-22 10:44:00 -04:00
Hal Gentz
4a103387e5 Add contact info. (#818)
Signed-off-by: Hal Gentz <zegentzy@protonmail.com>
2019-03-19 22:20:03 -04:00
Osspial
b6ca584ecf On Windows, fix CursorMoved(0, 0) getting sent on focus (#819)
* On Windows, fix CursorMoved(0, 0) getting sent on focus

* Add changelog entry
2019-03-19 22:19:41 -04:00
Osspial
e0340d52b0 Update winit to 0.19.0 (#798)
* Update winit to 0.19.0

* Update date for 0.19
2019-03-06 21:50:13 -05:00
Hal Gentz
f928a4b917 Use XRRGetScreenResourcesCurrent when avail. (#801)
* Use `XRRGetScreenResourcesCurrent` when avail.

Signed-off-by: Hal Gentz <zegentzy@protonmail.com>

* Changelog

Signed-off-by: Hal Gentz <zegentzy@protonmail.com>
2019-03-05 20:58:14 -05:00
Osspial
c5d22fda2b Ignore the AltGr key when populating ModifersState (#763)
* When building ModifiersState, ignore AltGr on Windows

* Add CHANGELOG entry

* Also filter out Control when pressing AltGr
2019-03-05 17:55:01 -05:00
Riku Salminen
9ea2810b46 x11: thread safe replacement for XNextEvent (#782)
XNextEvent will block for input while holding the global Xlib mutex.

This will cause a deadlock in even the most trivial multi-threaded
application because OpenGL functions will need to hold the Xlib mutex
too.

Add EventsLoop::poll_one_event and EventsLoop::wait_for_input to provide
thread-safe functions to poll and wait events from the X11 event queue
using unix select(2) and XCheckIfEvent.

This is a somewhat ugly workaround to an ugly problem.

Fixes #779
2019-02-24 18:02:55 -05:00
Michael Palmos
9a23ec3c37 Fix incorrect keycodes when using a non-US keyboard layout. (#755)
* Fix incorrect keycodes when using a non-US keyboard layout.

This commit fixes the issue described in #752, and uses the advised
method to fix it.

* Style fixes

Co-Authored-By: Toqozz <toqoz@hotmail.com>

* Refactoring of macOS `virtualkeycode` fix (#752)

* Applies requested changes as per pull request discussion (#755).
2019-02-23 15:41:55 -05:00
Torkel Danielsson
84c812e568 Handle horizontal wheel input (Windows) (#792)
* add handler for horizontal wheel input

* add changlelog message re now handling horiz scroll on windows
2019-02-22 09:31:16 -05:00
trimental
f0ce5b0c8d On wayland, fix with_title() not setting the windows title (#770) 2019-02-22 09:30:59 -05:00
33 changed files with 767 additions and 157 deletions

View File

@@ -2,3 +2,4 @@
- [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users - [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior - [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
- [ ] Created an example program if it would help users understand this functionality - [ ] Created an example program if it would help users understand this functionality
- [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "deps/apk-builder"] [submodule "deps/apk-builder"]
path = deps/apk-builder path = deps/apk-builder
url = https://github.com/tomaka/android-rs-glue url = https://github.com/rust-windowing/android-rs-glue

View File

@@ -1,12 +1,36 @@
# Unreleased # Unreleased
# Version 0.19.2 (2019-07-29)
- On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor.
# Version 0.19.1 (2019-04-08)
- On Wayland, added a `get_wayland_display` function to `EventsLoopExt`.
- On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus.
- On macOS, fix command key event left and right reverse.
- On FreeBSD, NetBSD, and OpenBSD, fix build of X11 backend.
- On Windows, fix icon not showing up in corner of window.
- On X11, change DPI scaling factor behavior. First, winit tries to read it from "Xft.dpi" XResource, and uses DPI calculation from xrandr dimensions as fallback behavior.
# Version 0.19.0 (2019-03-06)
- On X11, we will use the faster `XRRGetScreenResourcesCurrent` function instead of `XRRGetScreenResources` when available.
- On macOS, fix keycodes being incorrect when using a non-US keyboard layout.
- On Wayland, fix `with_title()` not setting the windows title
- On Wayland, add `set_wayland_theme()` to control client decoration color theme - On Wayland, add `set_wayland_theme()` to control client decoration color theme
- Added serde serialization to `os::unix::XWindowType`. - Added serde serialization to `os::unix::XWindowType`.
- **Breaking:** `image` crate upgraded to 0.21. This is exposed as part of the `icon_loading` API. - **Breaking:** `image` crate upgraded to 0.21. This is exposed as part of the `icon_loading` API.
- On X11, make event loop thread safe by replacing XNextEvent with select(2) and XCheckIfEvent
- On Windows, fix malformed function pointer typecast that could invoke undefined behavior. - On Windows, fix malformed function pointer typecast that could invoke undefined behavior.
- Refactored Windows state/flag-setting code. - Refactored Windows state/flag-setting code.
- On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on. - On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on.
- On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area. - On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area.
- On Windows, only vertical mouse wheel events were handled. Now, horizontal mouse wheel events are also handled.
- On Windows, ignore the AltGr key when populating the `ModifersState` type.
- On Linux, the numpad's add, subtract and divide keys are now mapped to the `Add`, `Subtract` and `Divide` virtual key codes
- On macOS, the numpad's subtract key has been added to the `Subtract` mapping
- On Wayland, the numpad's home, end, page up and page down keys are now mapped to the `Home`, `End`, `PageUp` and `PageDown` virtual key codes
# Version 0.18.1 (2018-12-30) # Version 0.18.1 (2018-12-30)

View File

@@ -1,28 +1,14 @@
# PLEASE MAKE PRs AGAINST THE `eventloop-2.0` BRANCH.
All development work for our next version is being done against that branch. Refer to [#459](https://github.com/rust-windowing/winit/issues/459) and [the associated milestone](https://github.com/rust-windowing/winit/milestone/2) for details on what that branch changes.
# Winit Contributing Guidelines # Winit Contributing Guidelines
## Scope ## Scope
[See `FEATURES.md`](./FEATURES.md). When requesting or implementing a new Winit feature, you should
consider whether or not it's directly related to window creation or input handling. If it isn't, it
may be worth creating a separate crate that extends Winit's API to add that functionality.
Winit aims to provide a generic platform abstracting the main graphic platforms (Windows, macOS, X11,
Wayland, Android, iOS and the web platform via Emscripten).
Most platforms expose capabilities that cannot be meaningfully transposed to the others. Winit does not
aim to support every single functionality of every platform, but rather to abstract the set of
capabilities that is common to all platforms. In this context, APIs exposed in winit can be split into
different "support levels":
- Tier 1: features which are in the main scope of winit. They are part of the common API of winit, and
are taken care of by the maintainers. Any part of these features that is not working correctly is
considered a bug in winit.
- Tier 2: some platform-specific features can be sufficiently fundamental to the platform that winit can
integrate support for them in the platform-specific part of the API. These features are not considered
directly handled by the maintainers of winit. If you have a strong incentive to have such a feature
integrated in winit, consider implementing it and proposing yourself to maintain it in the future.
- Tier 3: these features are not directly exposed by winit, but rather can be implemented using the
raw handles to the underlying platform that winit exposes. If your feature of interest is rather
niche, this is probably where it belongs.
The exact list of supported Tier 1 features is tracked in this issue:
[#252](https://github.com/tomaka/winit/issues/252).
## Reporting an issue ## Reporting an issue
@@ -44,6 +30,9 @@ When making a code contribution to winit, before opening your pull request, plea
- you left comments in your code explaining any part that is not straightforward, so that the - you left comments in your code explaining any part that is not straightforward, so that the
maintainers and future contributors don't have to try to guess what your code is supposed to do maintainers and future contributors don't have to try to guess what your code is supposed to do
- your PR adds an entry to the changelog file if the introduced change is relevant to winit users - your PR adds an entry to the changelog file if the introduced change is relevant to winit users
- if your PR affects the platform compatibility of one or more features or adds another feature, the
relevant sections in [`FEATURES.md`](https://github.com/rust-windowing/winit/blob/master/FEATURES.md#features)
should be updated.
Once your PR is open, you can ask for review by a maintainer of your platform. Winit's merging policy Once your PR is open, you can ask for review by a maintainer of your platform. Winit's merging policy
is that a PR must be approved by at least two maintainers of winit before being merged, including is that a PR must be approved by at least two maintainers of winit before being merged, including
@@ -56,15 +45,14 @@ backends of winit. As such, depending on your platform of interest, your contact
This table summarizes who can be contacted in which case, with the following legend: This table summarizes who can be contacted in which case, with the following legend:
- `M`: is a main maintainer for this platform - `M` - Maintainer: is a main maintainer for this platform
- `R`: can review code for this platform - `C` - Collaborator: can review code and address issues on this platform
- `T`: has the ability of testing the platform - `T` - Tester: has the ability of testing the platform
- ` `: knows nothing of this platform - ` `: knows nothing of this platform
| Platform | Windows | macOS | X11 | Wayland | Android | iOS | Emscripten | | Platform | Windows | macOS | X11 | Wayland | Android | iOS | Emscripten |
| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | | :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| @francesca64 | R | M | M | | M | R | | | @mitchmindtree | T | | T | T | | | |
| @mitchmindtree | T | | T | T | | | | | @Osspial | M | | T | T | T | | T |
| @Osspial | M | | T | T | T | | T | | @vberger | | | T | M | | | |
| @vberger | | | T | M | | | | | @mtak- | | T | | | T | M | |
| @mtak- | | T | | | T | M | |

View File

@@ -1,12 +1,12 @@
[package] [package]
name = "winit" name = "winit"
version = "0.18.1" version = "0.19.3"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"] authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library." description = "Cross-platform window creation library."
keywords = ["windowing"] keywords = ["windowing"]
license = "Apache-2.0" license = "Apache-2.0"
readme = "README.md" readme = "README.md"
repository = "https://github.com/tomaka/winit" repository = "https://github.com/rust-windowing/winit"
documentation = "https://docs.rs/winit" documentation = "https://docs.rs/winit"
categories = ["gui"] categories = ["gui"]
@@ -66,5 +66,5 @@ features = [
wayland-client = { version = "0.21", features = [ "dlopen", "egl", "cursor"] } wayland-client = { version = "0.21", features = [ "dlopen", "egl", "cursor"] }
smithay-client-toolkit = "0.4.3" smithay-client-toolkit = "0.4.3"
x11-dl = "2.18.3" x11-dl = "2.18.3"
parking_lot = "0.7" parking_lot = "0.9"
percent-encoding = "1.0" percent-encoding = "2.0"

209
FEATURES.md Normal file
View File

@@ -0,0 +1,209 @@
# Winit Scope
Winit aims to expose an interface that abstracts over window creation and input handling, and can
be used to create both games and applications. It supports the main graphical platforms:
- Desktop
- Windows
- macOS
- Unix
- via X11
- via Wayland
- Mobile
- iOS
- Android
- Web
- via Emscripten
- via WASM
Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not
aim to support every single feature of every platform, but rather to abstract over the common features
available everywhere. In this context, APIs exposed in winit can be split into different "support tiers":
- **Core:** Features that are essential to providing a well-formed abstraction over each platform's
windowing and input APIs.
- **Platform:** Platform-specific features that can't be meaningfully exposed through a common API and
cannot be implemented outside of Winit without exposing a significant amount of Winit's internals
or interfering with Winit's abstractions.
- **Usability:** Features that are not strictly essential to Winit's functionality, but provide meaningful
usability improvements and cannot be reasonably implemented in an external crate. These are
generally optional and exposed through Cargo features.
Core features are taken care of by the core Winit maintainers. Platform features are not.
When a platform feature is submitted, the submitter is considered the expert in the
feature and may be asked to support the feature should it break in the future.
Winit ***does not*** directly expose functionality for drawing inside windows or creating native
menus, but ***does*** commit to providing APIs that higher-level crates can use to implement that
functionality.
## `1.0` and stability
When all core features are implemented to the satisfaction of the Winit maintainers, Winit 1.0 will
be released and the library will enter maintenance mode. For the most part, new core features will not
be added past this point. New platform features may be accepted and exposed through point releases.
### Tier upgrades
Some platform features could in theory be exposed across multiple platforms, but have not gone
through the implementation work necessary to function on all platforms. When one of these features
gets implemented across all platforms, a PR can be opened to upgrade the feature to a core feature.
If that gets accepted, the platform-specific functions gets deprecated and become permanently
exposed through the core, cross-platform API.
# Features
## Extending this section
If your PR makes notable changes to Winit's features, please update this section as follows:
- If your PR adds a new feature, add a brief description to the relevant section. If the feature is a core
feature, add a row to the feature matrix and describe what platforms the feature has been implemented on.
- If your PR begins a new API rework, add a row to the `Pending API Reworks` table. If the PR implements the
API rework on all relevant platforms, please move it to the `Completed API Reworks` table.
- If your PR implements an already-existing feature on a new platform, either mark the feature as *completed*,
or mark it as *mostly completed* and link to an issue describing the problems with the implementation.
## Core
### Windowing
- **Window initialization**: Winit allows the creation of a window
- **Providing pointer to init OpenGL**: Winit provides the necessary pointers to initialize a working opengl context
- **Providing pointer to init Vulkan**: Same as OpenGL but for Vulkan
- **Window decorations**: The windows created by winit are properly decorated, and the decorations can
be deactivated
- **Window decorations toggle**: Decorations can be turned on or off after window creation
- **Window resizing**: The windows created by winit can be resized and generate the appropriate events
when they are. The application can precisely control its window size if desired.
- **Window resize increments**: When the window gets resized, the application can choose to snap the window's
size to specific values.
- **Window transparency**: Winit allows the creation of windows with a transparent background.
- **Window maximization**: The windows created by winit can be maximized upon creation.
- **Window maximization toggle**: The windows created by winit can be maximized and unmaximized after
creation.
- **Fullscreen**: The windows created by winit can be put into fullscreen mode.
- **Fullscreen toggle**: The windows created by winit can be switched to and from fullscreen after
creation.
- **HiDPI support**: Winit assists developers in appropriately scaling HiDPI content.
- **Popup / modal windows**: Windows can be created relative to the client area of other windows, and parent
windows can be disabled in favor of popup windows. This feature also guarantees that popup windows
get drawn above their owner.
### System Information
- **Monitor list**: Retrieve the list of monitors and their metadata, including which one is primary.
### Input Handling
- **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events.
- **Mouse set location**: Forcibly changing the location of the pointer.
- **Cursor grab**: Locking the cursor so it cannot exit the client area of a window.
- **Cursor icon**: Changing the cursor icon, or hiding the cursor.
- **Touch events**: Single-touch events.
- **Multitouch**: Multi-touch events, including cancellation of a gesture.
- **Keyboard events**: Properly processing keyboard events using the user-specified keymap and
translating keypresses into UTF-8 characters, handling dead keys and IMEs.
- **Drag & Drop**: Dragging content into winit, detecting when content enters, drops, or if the drop is cancelled.
- **Raw Device Events**: Capturing input from input devices without any OS filtering.
- **Gamepad/Joystick events**: Capturing input from gampads and joysticks.
- **Device movement events:**: Capturing input from the device gyroscope and accelerometer.
## Platform
### Windows
* Setting the taskbar icon
* Setting the parent window
* `WS_EX_NOREDIRECTIONBITMAP` support
### macOS
* Window activation policy
* Window movable by background
* Transparent titlebar
* Hidden titlebar
* Hidden titlebar buttons
* Full-size content view
### Unix
* Window urgency
* X11 Window Class
* X11 Override Redirect Flag
* GTK Theme Variant
* Base window size
## Usability
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)
## Compatibility Matrix
Legend:
- ✔️: Works as intended
- ▢: Mostly works but some bugs are known
- ❌: Missing feature or large bugs making it unusable
- **N/A**: Not applicable for this platform
- ❓: Unknown status
### Windowing
|Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |Emscripten|
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |❓ |
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |
|Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A** |
|Window decorations |✔️ |✔️ |✔️ |▢[#306] |**N/A**|**N/A**|**N/A** |
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
|Window resizing |✔️ |▢[#219]|✔️ |▢[#306] |**N/A**|**N/A**|❓ |
|Window resize increments |❌ |❌ |❌ |❌ |❌ |❌ |❌ |
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |
|HiDPI support |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |✔️ |
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |❌ |
### System information
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
|------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Monitor list |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |
### Input handling
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|**N/A** |
|Cursor grab |✔️ |▢[#165] |▢[#242] |❌[#306] |**N/A**|**N/A**|✔️ |
|Cursor icon |✔️ |✔️ |✔️ |❌[#306] |**N/A**|**N/A**|❌ |
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |
|Multitouch |❓ |❌ |✔️ |✔️ |❓ |❌ |❌ |
|Keyboard events |✔️ |✔️ |✔️ |✔️ |❓ |❌ |✔️ |
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |❌[#306] |**N/A**|**N/A**|❓ |
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❌ |
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❌ |
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❌ |
### Pending API Reworks
Changes in the API that have been agreed upon but aren't implemented across all platforms.
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |✔️ |
|Event Loop 2.0 ([#459]) |✔️ |❌ |❌ |✔️ |❌ |❌ |❌ |
|Keyboard Input ([#812]) |❌ |❌ |❌ |❌ |❌ |❌ |❌ |
### Completed API Reworks
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Emscripten|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
[#165]: https://github.com/rust-windowing/winit/issues/165
[#219]: https://github.com/rust-windowing/winit/issues/219
[#242]: https://github.com/rust-windowing/winit/issues/242
[#306]: https://github.com/rust-windowing/winit/issues/306
[#315]: https://github.com/rust-windowing/winit/issues/315
[#319]: https://github.com/rust-windowing/winit/issues/319
[#33]: https://github.com/rust-windowing/winit/issues/33
[#459]: https://github.com/rust-windowing/winit/issues/459
[#5]: https://github.com/rust-windowing/winit/issues/5
[#63]: https://github.com/rust-windowing/winit/issues/63
[#720]: https://github.com/rust-windowing/winit/issues/720
[#721]: https://github.com/rust-windowing/winit/issues/721
[#750]: https://github.com/rust-windowing/winit/issues/750
[#804]: https://github.com/rust-windowing/winit/issues/804
[#812]: https://github.com/rust-windowing/winit/issues/812

11
HALL_OF_CHAMPIONS.md Normal file
View File

@@ -0,0 +1,11 @@
# Hall of Champions
The Winit maintainers would like to recognize the following former Winit
contributors, without whom Winit would not exist in its current form. We thank
them deeply for their time and efforts, and wish them best of luck in their
future endeavors:
* @tomaka: For creating the Winit project and guiding it through its early
years of existence.
* @francesca64: For taking over the responsibility of maintaining almost every
Winit backend, and standardizing HiDPI support across all of them

View File

@@ -1,17 +1,35 @@
# This branch has been archived
Please direct all work against `master`.
--------
# winit - Cross-platform window creation and management in Rust # winit - Cross-platform window creation and management in Rust
[![](http://meritbadge.herokuapp.com/winit)](https://crates.io/crates/winit) [![Crates.io](https://img.shields.io/crates/v/winit.svg)](https://crates.io/crates/winit)
[![Docs.rs](https://docs.rs/winit/badge.svg)](https://docs.rs/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://travis-ci.org/rust-windowing/winit.svg?branch=master)](https://travis-ci.org/rust-windowing/winit)
[![Build status](https://ci.appveyor.com/api/projects/status/5h87hj0g4q2xe3j9/branch/master?svg=true)](https://ci.appveyor.com/project/tomaka/winit/branch/master) [![Build status](https://ci.appveyor.com/api/projects/status/hr89but4x1n3dphq/branch/master?svg=true)](https://ci.appveyor.com/project/Osspial/winit/branch/master)
```toml ```toml
[dependencies] [dependencies]
winit = "0.18.1" winit = "0.19.3"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)
For features _within_ the scope of winit, see [FEATURES.md](FEATURES.md).
For features _outside_ the scope of winit, see [Missing features provided by other crates](https://github.com/rust-windowing/winit/wiki/Missing-features-provided-by-other-crates) in the wiki.
## Contact Us
Join us in any of these:
[![Freenode](https://img.shields.io/badge/freenode.net-%23glutin-red.svg)](http://webchat.freenode.net?channels=%23glutin&uio=MTY9dHJ1ZSYyPXRydWUmND10cnVlJjExPTE4NSYxMj10cnVlJjE1PXRydWU7a)
[![Matrix](https://img.shields.io/badge/Matrix-%23Glutin%3Amatrix.org-blueviolet.svg)](https://matrix.to/#/#Glutin:matrix.org)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tomaka/glutin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Usage ## Usage
Winit is a window creation and management library. It can create windows and lets you handle Winit is a window creation and management library. It can create windows and lets you handle

View File

@@ -82,6 +82,15 @@ fn main() {
window.set_fullscreen(Some(window.get_current_monitor())); window.set_fullscreen(Some(window.get_current_monitor()));
} }
} }
(winit::VirtualKeyCode::S, winit::ElementState::Pressed) => {
println!("window.get_fullscreen {:?}", window.get_fullscreen());
#[cfg(target_os = "macos")]
{
use winit::os::macos::WindowExt;
println!("window.get_simple_fullscreen {:?}", WindowExt::get_simple_fullscreen(&window));
}
}
(winit::VirtualKeyCode::M, winit::ElementState::Pressed) => { (winit::VirtualKeyCode::M, winit::ElementState::Pressed) => {
is_maximized = !is_maximized; is_maximized = !is_maximized;
window.set_maximized(is_maximized); window.set_maximized(is_maximized);

View File

@@ -23,6 +23,9 @@ pub trait WindowExt {
/// - `true`: the dock icon will bounce until the application is focused. /// - `true`: the dock icon will bounce until the application is focused.
fn request_user_attention(&self, is_critical: bool); fn request_user_attention(&self, is_critical: bool);
/// Returns whether or not the window is in simple fullscreen mode.
fn get_simple_fullscreen(&self) -> bool;
/// Toggles a fullscreen mode that doesn't require a new macOS space. /// Toggles a fullscreen mode that doesn't require a new macOS space.
/// Returns a boolean indicating whether the transition was successful (this /// Returns a boolean indicating whether the transition was successful (this
/// won't work if the window was already in the native fullscreen). /// won't work if the window was already in the native fullscreen).
@@ -49,6 +52,11 @@ impl WindowExt for Window {
self.window.request_user_attention(is_critical) self.window.request_user_attention(is_critical)
} }
#[inline]
fn get_simple_fullscreen(&self) -> bool {
self.window.get_simple_fullscreen()
}
#[inline] #[inline]
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool { fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
self.window.set_simple_fullscreen(fullscreen) self.window.set_simple_fullscreen(fullscreen)

View File

@@ -113,6 +113,13 @@ pub trait EventsLoopExt {
#[doc(hidden)] #[doc(hidden)]
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>; fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
/// Returns a pointer to the `wl_display` object of wayland that is used by this `EventsLoop`.
///
/// Returns `None` if the `EventsLoop` doesn't use wayland (if it uses xlib for example).
///
/// The pointer will become invalid when the glutin `EventsLoop` is destroyed.
fn get_wayland_display(&self) -> Option<*mut raw::c_void>;
} }
impl EventsLoopExt for EventsLoop { impl EventsLoopExt for EventsLoop {
@@ -152,6 +159,14 @@ impl EventsLoopExt for EventsLoop {
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> { fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
self.events_loop.x_connection().cloned() self.events_loop.x_connection().cloned()
} }
#[inline]
fn get_wayland_display(&self) -> Option<*mut raw::c_void> {
match self.events_loop {
LinuxEventsLoop::Wayland(ref e) => Some(e.get_display().c_ptr() as *mut _),
_ => None
}
}
} }
/// Additional methods on `Window` that are specific to Unix. /// Additional methods on `Window` that are specific to Unix.

View File

@@ -368,6 +368,13 @@ impl Window {
// Android has single screen maximized apps so nothing to do // Android has single screen maximized apps so nothing to do
} }
#[inline]
pub fn get_fullscreen(&self) -> Option<RootMonitorId> {
// N/A
// Android has single screen maximized apps so nothing to do
None
}
#[inline] #[inline]
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) { pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
// N/A // N/A

View File

@@ -580,6 +580,11 @@ impl Window {
// iOS has single screen maximized apps so nothing to do // iOS has single screen maximized apps so nothing to do
} }
#[inline]
pub fn get_fullscreen(&self) -> Option<::MonitorId> {
None
}
#[inline] #[inline]
pub fn set_fullscreen(&self, _monitor: Option<::MonitorId>) { pub fn set_fullscreen(&self, _monitor: Option<::MonitorId>) {
// iOS has single screen maximized apps so nothing to do // iOS has single screen maximized apps so nothing to do

View File

@@ -473,6 +473,13 @@ impl Window {
// iOS has single screen maximized apps so nothing to do // iOS has single screen maximized apps so nothing to do
} }
#[inline]
pub fn get_fullscreen(&self) -> Option<RootMonitorId> {
// N/A
// iOS has single screen maximized apps so nothing to do
None
}
#[inline] #[inline]
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) { pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
// N/A // N/A

View File

@@ -6,7 +6,7 @@ use std::os::raw::{c_void, c_char, c_int};
pub const RTLD_LAZY: c_int = 0x001; pub const RTLD_LAZY: c_int = 0x001;
pub const RTLD_NOW: c_int = 0x002; pub const RTLD_NOW: c_int = 0x002;
#[link="dl"] #[link(name = "dl")]
extern { extern {
pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void; pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
pub fn dlerror() -> *mut c_char; pub fn dlerror() -> *mut c_char;

View File

@@ -301,6 +301,15 @@ impl Window {
} }
} }
#[inline]
pub fn get_fullscreen(&self) -> Option<RootMonitorId> {
match self {
&Window::X(ref w) => w.get_fullscreen(),
&Window::Wayland(ref w) => w.get_fullscreen()
.map(|monitor_id| RootMonitorId { inner: MonitorId::Wayland(monitor_id) })
}
}
#[inline] #[inline]
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) { pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
match self { match self {

View File

@@ -229,6 +229,10 @@ impl EventsLoop {
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> { pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
get_available_monitors(&self.env.outputs) get_available_monitors(&self.env.outputs)
} }
pub fn get_display(&self) -> &Display {
&*self.display
}
} }
/* /*

View File

@@ -312,6 +312,13 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma), keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma),
keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter), keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter),
keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals), keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals),
keysyms::XKB_KEY_KP_Add => Some(VirtualKeyCode::Add),
keysyms::XKB_KEY_KP_Subtract => Some(VirtualKeyCode::Subtract),
keysyms::XKB_KEY_KP_Divide => Some(VirtualKeyCode::Divide),
keysyms::XKB_KEY_KP_Page_Up => Some(VirtualKeyCode::PageUp),
keysyms::XKB_KEY_KP_Page_Down => Some(VirtualKeyCode::PageDown),
keysyms::XKB_KEY_KP_Home => Some(VirtualKeyCode::Home),
keysyms::XKB_KEY_KP_End => Some(VirtualKeyCode::End),
// => Some(VirtualKeyCode::OEM102), // => Some(VirtualKeyCode::OEM102),
// => Some(VirtualKeyCode::Period), // => Some(VirtualKeyCode::Period),
// => Some(VirtualKeyCode::Playpause), // => Some(VirtualKeyCode::Playpause),

View File

@@ -16,21 +16,9 @@ mod window;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId; pub struct DeviceId;
impl DeviceId {
pub unsafe fn dummy() -> Self {
DeviceId
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(usize); pub struct WindowId(usize);
impl WindowId {
pub unsafe fn dummy() -> Self {
WindowId(0)
}
}
#[inline] #[inline]
fn make_wid(s: &Proxy<wl_surface::WlSurface>) -> WindowId { fn make_wid(s: &Proxy<wl_surface::WlSurface>) -> WindowId {
WindowId(s.c_ptr() as usize) WindowId(s.c_ptr() as usize)

View File

@@ -7,7 +7,7 @@ use platform::{MonitorId as PlatformMonitorId, PlatformSpecificWindowBuilderAttr
use window::MonitorId as RootMonitorId; use window::MonitorId as RootMonitorId;
use sctk::surface::{get_dpi_factor, get_outputs}; use sctk::surface::{get_dpi_factor, get_outputs};
use sctk::window::{ConceptFrame, Event as WEvent, Window as SWindow, Theme}; use sctk::window::{ConceptFrame, Event as WEvent, State as WState, Window as SWindow, Theme};
use sctk::reexports::client::{Display, Proxy}; use sctk::reexports::client::{Display, Proxy};
use sctk::reexports::client::protocol::{wl_seat, wl_surface}; use sctk::reexports::client::protocol::{wl_seat, wl_surface};
use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests; use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
@@ -24,6 +24,7 @@ pub struct Window {
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>), kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
display: Arc<Display>, display: Arc<Display>,
need_frame_refresh: Arc<Mutex<bool>>, need_frame_refresh: Arc<Mutex<bool>>,
fullscreen: Arc<Mutex<bool>>,
} }
impl Window { impl Window {
@@ -31,6 +32,7 @@ impl Window {
let (width, height) = attributes.dimensions.map(Into::into).unwrap_or((800, 600)); let (width, height) = attributes.dimensions.map(Into::into).unwrap_or((800, 600));
// Create the window // Create the window
let size = Arc::new(Mutex::new((width, height))); let size = Arc::new(Mutex::new((width, height)));
let fullscreen = Arc::new(Mutex::new(false));
let window_store = evlp.store.clone(); let window_store = evlp.store.clone();
let surface = evlp.env.create_surface(move |dpi, surface| { let surface = evlp.env.create_surface(move |dpi, surface| {
@@ -45,12 +47,15 @@ impl Window {
surface.clone(), surface.clone(),
(width, height), (width, height),
move |event| match event { move |event| match event {
WEvent::Configure { new_size, .. } => { WEvent::Configure { new_size, states } => {
let mut store = window_store.lock().unwrap(); let mut store = window_store.lock().unwrap();
let is_fullscreen = states.contains(&WState::Fullscreen);
for window in &mut store.windows { for window in &mut store.windows {
if window.surface.equals(&my_surface) { if window.surface.equals(&my_surface) {
window.newsize = new_size; window.newsize = new_size;
window.need_refresh = true; window.need_refresh = true;
*(window.fullscreen.lock().unwrap()) = is_fullscreen;
*(window.need_frame_refresh.lock().unwrap()) = true; *(window.need_frame_refresh.lock().unwrap()) = true;
return; return;
} }
@@ -81,6 +86,8 @@ impl Window {
frame.set_app_id(app_id); frame.set_app_id(app_id);
} }
frame.set_title(attributes.title);
for &(_, ref seat) in evlp.seats.lock().unwrap().iter() { for &(_, ref seat) in evlp.seats.lock().unwrap().iter() {
frame.new_seat(seat); frame.new_seat(seat);
} }
@@ -112,6 +119,7 @@ impl Window {
closed: false, closed: false,
newsize: None, newsize: None,
size: size.clone(), size: size.clone(),
fullscreen: fullscreen.clone(),
need_refresh: false, need_refresh: false,
need_frame_refresh: need_frame_refresh.clone(), need_frame_refresh: need_frame_refresh.clone(),
surface: surface.clone(), surface: surface.clone(),
@@ -130,6 +138,7 @@ impl Window {
size: size, size: size,
kill_switch: (kill_switch, evlp.cleanup_needed.clone()), kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
need_frame_refresh: need_frame_refresh, need_frame_refresh: need_frame_refresh,
fullscreen: fullscreen,
}) })
} }
@@ -221,6 +230,14 @@ impl Window {
} }
} }
pub fn get_fullscreen(&self) -> Option<MonitorId> {
if *(self.fullscreen.lock().unwrap()) {
Some(self.get_current_monitor())
} else {
None
}
}
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) { pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
if let Some(RootMonitorId { if let Some(RootMonitorId {
inner: PlatformMonitorId::Wayland(ref monitor_id), inner: PlatformMonitorId::Wayland(ref monitor_id),
@@ -300,6 +317,7 @@ struct InternalWindow {
surface: Proxy<wl_surface::WlSurface>, surface: Proxy<wl_surface::WlSurface>,
newsize: Option<(u32, u32)>, newsize: Option<(u32, u32)>,
size: Arc<Mutex<(u32, u32)>>, size: Arc<Mutex<(u32, u32)>>,
fullscreen: Arc<Mutex<bool>>,
need_refresh: bool, need_refresh: bool,
need_frame_refresh: Arc<Mutex<bool>>, need_frame_refresh: Arc<Mutex<bool>>,
closed: bool, closed: bool,

View File

@@ -82,11 +82,11 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
ffi::XK_KP_Delete => events::VirtualKeyCode::Delete, ffi::XK_KP_Delete => events::VirtualKeyCode::Delete,
ffi::XK_KP_Equal => events::VirtualKeyCode::NumpadEquals, ffi::XK_KP_Equal => events::VirtualKeyCode::NumpadEquals,
//ffi::XK_KP_Multiply => events::VirtualKeyCode::NumpadMultiply, //ffi::XK_KP_Multiply => events::VirtualKeyCode::NumpadMultiply,
//ffi::XK_KP_Add => events::VirtualKeyCode::NumpadAdd, ffi::XK_KP_Add => events::VirtualKeyCode::Add,
//ffi::XK_KP_Separator => events::VirtualKeyCode::Kp_separator, //ffi::XK_KP_Separator => events::VirtualKeyCode::Kp_separator,
//ffi::XK_KP_Subtract => events::VirtualKeyCode::NumpadSubtract, ffi::XK_KP_Subtract => events::VirtualKeyCode::Subtract,
//ffi::XK_KP_Decimal => events::VirtualKeyCode::Kp_decimal, //ffi::XK_KP_Decimal => events::VirtualKeyCode::Kp_decimal,
//ffi::XK_KP_Divide => events::VirtualKeyCode::NumpadDivide, ffi::XK_KP_Divide => events::VirtualKeyCode::Divide,
ffi::XK_KP_0 => events::VirtualKeyCode::Numpad0, ffi::XK_KP_0 => events::VirtualKeyCode::Numpad0,
ffi::XK_KP_1 => events::VirtualKeyCode::Numpad1, ffi::XK_KP_1 => events::VirtualKeyCode::Numpad1,
ffi::XK_KP_2 => events::VirtualKeyCode::Numpad2, ffi::XK_KP_2 => events::VirtualKeyCode::Numpad2,

View File

@@ -19,6 +19,13 @@ use std::collections::HashMap;
use std::ffi::CStr; use std::ffi::CStr;
use std::ops::Deref; use std::ops::Deref;
use std::os::raw::*; use std::os::raw::*;
use libc::{select, fd_set, FD_SET, FD_ZERO, FD_ISSET, EINTR, EINVAL, ENOMEM, EBADF};
#[cfg(target_os = "linux")]
use libc::__errno_location;
#[cfg(target_os = "freebsd")]
use libc::__error as __errno_location;
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
use libc::__errno as __errno_location;
use std::sync::{Arc, mpsc, Weak}; use std::sync::{Arc, mpsc, Weak};
use std::sync::atomic::{self, AtomicBool}; use std::sync::atomic::{self, AtomicBool};
@@ -185,6 +192,70 @@ impl EventsLoop {
} }
} }
unsafe fn poll_one_event(&mut self, event_ptr : *mut ffi::XEvent) -> bool {
// This function is used to poll and remove a single event
// from the Xlib event queue in a non-blocking, atomic way.
// XCheckIfEvent is non-blocking and removes events from queue.
// XNextEvent can't be used because it blocks while holding the
// global Xlib mutex.
// XPeekEvent does not remove events from the queue.
unsafe extern "C" fn predicate(
_display: *mut ffi::Display,
_event: *mut ffi::XEvent,
_arg : *mut c_char) -> c_int {
// This predicate always returns "true" (1) to accept all events
1
}
let result = (self.xconn.xlib.XCheckIfEvent)(
self.xconn.display,
event_ptr,
Some(predicate),
std::ptr::null_mut());
result != 0
}
unsafe fn wait_for_input(&mut self) {
// XNextEvent can not be used in multi-threaded applications
// because it is blocking for input while holding the global
// Xlib mutex.
// To work around this issue, first flush the X11 display, then
// use select(2) to wait for input to arrive
loop {
// First use XFlush to flush any buffered x11 requests
(self.xconn.xlib.XFlush)(self.xconn.display);
// Then use select(2) to wait for input data
let mut fds : fd_set = mem::uninitialized();
FD_ZERO(&mut fds);
FD_SET(self.xconn.x11_fd, &mut fds);
let err = select(
self.xconn.x11_fd + 1,
&mut fds, // read fds
std::ptr::null_mut(), // write fds
std::ptr::null_mut(), // except fds (could be used to detect errors)
std::ptr::null_mut()); // timeout
if err < 0 {
let errno_ptr = __errno_location();
let errno = *errno_ptr;
if errno == EINTR {
// try again if errno is EINTR
continue;
}
assert!(errno == EBADF || errno == EINVAL || errno == ENOMEM);
panic!("select(2) returned fatal error condition");
}
if FD_ISSET(self.xconn.x11_fd, &mut fds) {
break;
}
}
}
pub fn poll_events<F>(&mut self, mut callback: F) pub fn poll_events<F>(&mut self, mut callback: F)
where F: FnMut(Event) where F: FnMut(Event)
{ {
@@ -192,13 +263,9 @@ impl EventsLoop {
loop { loop {
// Get next event // Get next event
unsafe { unsafe {
// Ensure XNextEvent won't block if !self.poll_one_event(&mut xev) {
let count = (self.xconn.xlib.XPending)(self.xconn.display);
if count == 0 {
break; break;
} }
(self.xconn.xlib.XNextEvent)(self.xconn.display, &mut xev);
} }
self.process_event(&mut xev, &mut callback); self.process_event(&mut xev, &mut callback);
} }
@@ -210,7 +277,12 @@ impl EventsLoop {
let mut xev = unsafe { mem::uninitialized() }; let mut xev = unsafe { mem::uninitialized() };
loop { loop {
unsafe { (self.xconn.xlib.XNextEvent)(self.xconn.display, &mut xev) }; // Blocks as necessary unsafe {
while !self.poll_one_event(&mut xev) {
// block until input is available
self.wait_for_input();
}
};
let mut control_flow = ControlFlow::Continue; let mut control_flow = ControlFlow::Continue;

View File

@@ -131,9 +131,14 @@ impl XConnection {
fn query_monitor_list(&self) -> Vec<MonitorId> { fn query_monitor_list(&self) -> Vec<MonitorId> {
unsafe { unsafe {
let root = (self.xlib.XDefaultRootWindow)(self.display); let root = (self.xlib.XDefaultRootWindow)(self.display);
// WARNING: this function is supposedly very slow, on the order of hundreds of ms. let resources = if version_is_at_least(1, 3) {
// Upon failure, `resources` will be null. (self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
let resources = (self.xrandr.XRRGetScreenResources)(self.display, root); } else {
// WARNING: this function is supposedly very slow, on the order of hundreds of ms.
// Upon failure, `resources` will be null.
(self.xrandr.XRRGetScreenResources)(self.display, root)
};
if resources.is_null() { if resources.is_null() {
panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist."); panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist.");
} }

View File

@@ -23,7 +23,7 @@ pub fn calc_dpi_factor(
} }
// See http://xpra.org/trac/ticket/728 for more information. // See http://xpra.org/trac/ticket/728 for more information.
if width_mm == 0 || width_mm == 0 { if width_mm == 0 || height_mm == 0 {
warn!("XRandR reported that the display's 0mm in size, which is certifiably insane"); warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
return 1.0; return 1.0;
} }
@@ -79,6 +79,24 @@ impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr {
} }
impl XConnection { impl XConnection {
// Retrieve DPI from Xft.dpi property
pub unsafe fn get_xft_dpi(&self) -> Option<f64> {
(self.xlib.XrmInitialize)();
let resource_manager_str = (self.xlib.XResourceManagerString)(self.display);
if resource_manager_str == ptr::null_mut() {
return None;
}
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
let name : &str = "Xft.dpi:\t";
for pair in res.split("\n") {
if pair.starts_with(&name) {
let res = &pair[name.len()..];
return f64::from_str(&res).ok();
}
}
}
None
}
pub unsafe fn get_output_info( pub unsafe fn get_output_info(
&self, &self,
resources: *mut ffi::XRRScreenResources, resources: *mut ffi::XRRScreenResources,
@@ -101,10 +119,15 @@ impl XConnection {
(*output_info).nameLen as usize, (*output_info).nameLen as usize,
); );
let name = String::from_utf8_lossy(name_slice).into(); let name = String::from_utf8_lossy(name_slice).into();
let hidpi_factor = calc_dpi_factor( let hidpi_factor = if let Some(dpi) = self.get_xft_dpi() {
repr.get_dimensions(), dpi / 96.
((*output_info).mm_width as u64, (*output_info).mm_height as u64), } else {
); calc_dpi_factor(
repr.get_dimensions(),
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
)
};
(self.xrandr.XRRFreeOutputInfo)(output_info); (self.xrandr.XRRFreeOutputInfo)(output_info);
Some((name, hidpi_factor)) Some((name, hidpi_factor))
} }

View File

@@ -37,6 +37,7 @@ pub struct SharedState {
pub guessed_dpi: Option<f64>, pub guessed_dpi: Option<f64>,
pub last_monitor: Option<X11MonitorId>, pub last_monitor: Option<X11MonitorId>,
pub dpi_adjusted: Option<(f64, f64)>, pub dpi_adjusted: Option<(f64, f64)>,
pub fullscreen: Option<RootMonitorId>,
// Used to restore position after exiting fullscreen. // Used to restore position after exiting fullscreen.
pub restore_position: Option<(i32, i32)>, pub restore_position: Option<(i32, i32)>,
pub frame_extents: Option<util::FrameExtentsHeuristic>, pub frame_extents: Option<util::FrameExtentsHeuristic>,
@@ -533,8 +534,14 @@ impl UnownedWindow {
} }
} }
#[inline]
pub fn get_fullscreen(&self) -> Option<RootMonitorId> {
self.shared_state.lock().fullscreen.clone()
}
#[inline] #[inline]
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) { pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
self.shared_state.lock().fullscreen = monitor.clone();
self.set_fullscreen_inner(monitor) self.set_fullscreen_inner(monitor)
.flush() .flush()
.expect("Failed to change window fullscreen state"); .expect("Failed to change window fullscreen state");

View File

@@ -1,6 +1,7 @@
use std::ptr; use std::ptr;
use std::fmt; use std::fmt;
use std::error::Error; use std::error::Error;
use std::os::raw::c_int;
use libc; use libc;
use parking_lot::Mutex; use parking_lot::Mutex;
@@ -18,6 +19,7 @@ pub struct XConnection {
pub xinput2: ffi::XInput2, pub xinput2: ffi::XInput2,
pub xlib_xcb: ffi::Xlib_xcb, pub xlib_xcb: ffi::Xlib_xcb,
pub display: *mut ffi::Display, pub display: *mut ffi::Display,
pub x11_fd: c_int,
pub latest_error: Mutex<Option<XError>>, pub latest_error: Mutex<Option<XError>>,
} }
@@ -48,6 +50,11 @@ impl XConnection {
display display
}; };
// Get X11 socket file descriptor
let fd = unsafe {
(xlib.XConnectionNumber)(display)
};
Ok(XConnection { Ok(XConnection {
xlib, xlib,
xrandr, xrandr,
@@ -56,6 +63,7 @@ impl XConnection {
xinput2, xinput2,
xlib_xcb, xlib_xcb,
display, display,
x11_fd: fd,
latest_error: Mutex::new(None), latest_error: Mutex::new(None),
}) })
} }

View File

@@ -544,7 +544,67 @@ impl Proxy {
} }
} }
pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> { pub fn char_to_keycode(c: char) -> Option<events::VirtualKeyCode> {
// We only translate keys that are affected by keyboard layout.
//
// Note that since keys are translated in a somewhat "dumb" way (reading character)
// there is a concern that some combination, i.e. Cmd+char, causes the wrong
// letter to be received, and so we receive the wrong key.
//
// Implementation reference: https://github.com/WebKit/webkit/blob/82bae82cf0f329dbe21059ef0986c4e92fea4ba6/Source/WebCore/platform/cocoa/KeyEventCocoa.mm#L626
Some(match c {
'a' | 'A' => events::VirtualKeyCode::A,
'b' | 'B' => events::VirtualKeyCode::B,
'c' | 'C' => events::VirtualKeyCode::C,
'd' | 'D' => events::VirtualKeyCode::D,
'e' | 'E' => events::VirtualKeyCode::E,
'f' | 'F' => events::VirtualKeyCode::F,
'g' | 'G' => events::VirtualKeyCode::G,
'h' | 'H' => events::VirtualKeyCode::H,
'i' | 'I' => events::VirtualKeyCode::I,
'j' | 'J' => events::VirtualKeyCode::J,
'k' | 'K' => events::VirtualKeyCode::K,
'l' | 'L' => events::VirtualKeyCode::L,
'm' | 'M' => events::VirtualKeyCode::M,
'n' | 'N' => events::VirtualKeyCode::N,
'o' | 'O' => events::VirtualKeyCode::O,
'p' | 'P' => events::VirtualKeyCode::P,
'q' | 'Q' => events::VirtualKeyCode::Q,
'r' | 'R' => events::VirtualKeyCode::R,
's' | 'S' => events::VirtualKeyCode::S,
't' | 'T' => events::VirtualKeyCode::T,
'u' | 'U' => events::VirtualKeyCode::U,
'v' | 'V' => events::VirtualKeyCode::V,
'w' | 'W' => events::VirtualKeyCode::W,
'x' | 'X' => events::VirtualKeyCode::X,
'y' | 'Y' => events::VirtualKeyCode::Y,
'z' | 'Z' => events::VirtualKeyCode::Z,
'1' | '!' => events::VirtualKeyCode::Key1,
'2' | '@' => events::VirtualKeyCode::Key2,
'3' | '#' => events::VirtualKeyCode::Key3,
'4' | '$' => events::VirtualKeyCode::Key4,
'5' | '%' => events::VirtualKeyCode::Key5,
'6' | '^' => events::VirtualKeyCode::Key6,
'7' | '&' => events::VirtualKeyCode::Key7,
'8' | '*' => events::VirtualKeyCode::Key8,
'9' | '(' => events::VirtualKeyCode::Key9,
'0' | ')' => events::VirtualKeyCode::Key0,
'=' | '+' => events::VirtualKeyCode::Equals,
'-' | '_' => events::VirtualKeyCode::Minus,
']' | '}' => events::VirtualKeyCode::RBracket,
'[' | '{' => events::VirtualKeyCode::LBracket,
'\''| '"' => events::VirtualKeyCode::Apostrophe,
';' | ':' => events::VirtualKeyCode::Semicolon,
'\\'| '|' => events::VirtualKeyCode::Backslash,
',' | '<' => events::VirtualKeyCode::Comma,
'/' | '?' => events::VirtualKeyCode::Slash,
'.' | '>' => events::VirtualKeyCode::Period,
'`' | '~' => events::VirtualKeyCode::Grave,
_ => return None,
})
}
pub fn scancode_to_keycode(code: c_ushort) -> Option<events::VirtualKeyCode> {
Some(match code { Some(match code {
0x00 => events::VirtualKeyCode::A, 0x00 => events::VirtualKeyCode::A,
0x01 => events::VirtualKeyCode::S, 0x01 => events::VirtualKeyCode::S,
@@ -600,8 +660,8 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
0x33 => events::VirtualKeyCode::Back, 0x33 => events::VirtualKeyCode::Back,
//0x34 => unkown, //0x34 => unkown,
0x35 => events::VirtualKeyCode::Escape, 0x35 => events::VirtualKeyCode::Escape,
0x36 => events::VirtualKeyCode::LWin, 0x36 => events::VirtualKeyCode::RWin,
0x37 => events::VirtualKeyCode::RWin, 0x37 => events::VirtualKeyCode::LWin,
0x38 => events::VirtualKeyCode::LShift, 0x38 => events::VirtualKeyCode::LShift,
//0x39 => Caps lock, //0x39 => Caps lock,
0x3a => events::VirtualKeyCode::LAlt, 0x3a => events::VirtualKeyCode::LAlt,
@@ -623,6 +683,7 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
0x4a => events::VirtualKeyCode::VolumeDown, 0x4a => events::VirtualKeyCode::VolumeDown,
0x4b => events::VirtualKeyCode::Divide, 0x4b => events::VirtualKeyCode::Divide,
0x4c => events::VirtualKeyCode::NumpadEnter, 0x4c => events::VirtualKeyCode::NumpadEnter,
0x4e => events::VirtualKeyCode::Subtract,
//0x4d => unkown, //0x4d => unkown,
0x4e => events::VirtualKeyCode::Subtract, 0x4e => events::VirtualKeyCode::Subtract,
0x4f => events::VirtualKeyCode::F18, 0x4f => events::VirtualKeyCode::F18,
@@ -680,20 +741,19 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
}) })
} }
pub fn check_additional_virtual_key_codes( pub fn check_function_keys(
s: &Option<String> s: &String
) -> Option<events::VirtualKeyCode> { ) -> Option<events::VirtualKeyCode> {
if let &Some(ref s) = s { if let Some(ch) = s.encode_utf16().next() {
if let Some(ch) = s.encode_utf16().next() { return Some(match ch {
return Some(match ch { 0xf718 => events::VirtualKeyCode::F21,
0xf718 => events::VirtualKeyCode::F21, 0xf719 => events::VirtualKeyCode::F22,
0xf719 => events::VirtualKeyCode::F22, 0xf71a => events::VirtualKeyCode::F23,
0xf71a => events::VirtualKeyCode::F23, 0xf71b => events::VirtualKeyCode::F24,
0xf71b => events::VirtualKeyCode::F24, _ => return None,
_ => return None, })
})
}
} }
None None
} }
@@ -709,6 +769,16 @@ pub fn event_mods(event: cocoa::base::id) -> ModifiersState {
} }
} }
pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
// In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character,
// and there is no easy way to navtively retrieve the layout-dependent character.
// In winit, we use keycode to refer to the key's character, and so this function aligns
// AppKit's terminology with ours.
unsafe {
msg_send![event, keyCode]
}
}
unsafe fn modifier_event( unsafe fn modifier_event(
ns_event: cocoa::base::id, ns_event: cocoa::base::id,
keymask: NSEventModifierFlags, keymask: NSEventModifierFlags,
@@ -721,14 +791,14 @@ unsafe fn modifier_event(
} else { } else {
ElementState::Pressed ElementState::Pressed
}; };
let keycode = NSEvent::keyCode(ns_event);
let scancode = keycode as u32; let scancode = get_scancode(ns_event);
let virtual_keycode = to_virtual_key_code(keycode); let virtual_keycode = scancode_to_keycode(scancode);
Some(WindowEvent::KeyboardInput { Some(WindowEvent::KeyboardInput {
device_id: DEVICE_ID, device_id: DEVICE_ID,
input: KeyboardInput { input: KeyboardInput {
state, state,
scancode, scancode: scancode as u32,
virtual_keycode, virtual_keycode,
modifiers: event_mods(ns_event), modifiers: event_mods(ns_event),
}, },

View File

@@ -14,10 +14,11 @@ use objc::declare::ClassDecl;
use objc::runtime::{Class, Object, Protocol, Sel, BOOL, YES}; use objc::runtime::{Class, Object, Protocol, Sel, BOOL, YES};
use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId}; use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId};
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code, check_additional_virtual_key_codes}; use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, scancode_to_keycode, char_to_keycode, check_function_keys, get_scancode};
use platform::platform::util; use platform::platform::util;
use platform::platform::ffi::*; use platform::platform::ffi::*;
use platform::platform::window::{get_window_id, IdRef}; use platform::platform::window::{get_window_id, IdRef};
use events;
struct ViewState { struct ViewState {
window: id, window: id,
@@ -391,36 +392,68 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
} }
} }
fn get_characters(event: id) -> Option<String> { fn get_characters(event: id, ignore_modifiers: bool) -> String {
unsafe { unsafe {
let characters: id = msg_send![event, characters]; let characters: id = if ignore_modifiers {
msg_send![event, charactersIgnoringModifiers]
} else {
msg_send![event, characters]
};
assert_ne!(characters, nil);
let slice = slice::from_raw_parts( let slice = slice::from_raw_parts(
characters.UTF8String() as *const c_uchar, characters.UTF8String() as *const c_uchar,
characters.len(), characters.len(),
); );
let string = str::from_utf8_unchecked(slice); let string = str::from_utf8_unchecked(slice);
Some(string.to_owned())
string.to_owned()
} }
} }
// Retrieves a layout-independent keycode given an event.
fn retrieve_keycode(event: id) -> Option<events::VirtualKeyCode> {
#[inline]
fn get_code(ev: id, raw: bool) -> Option<events::VirtualKeyCode> {
let characters = get_characters(ev, raw);
characters.chars().next().map_or(None, |c| char_to_keycode(c))
}
// Cmd switches Roman letters for Dvorak-QWERTY layout, so we try modified characters first.
// If we don't get a match, then we fall back to unmodified characters.
let code = get_code(event, false)
.or_else(|| {
get_code(event, true)
});
// We've checked all layout related keys, so fall through to scancode.
// Reaching this code means that the key is layout-independent (e.g. Backspace, Return).
//
// We're additionally checking here for F21-F24 keys, since their keycode
// can vary, but we know that they are encoded
// in characters property.
code.or_else(|| {
let scancode = get_scancode(event);
scancode_to_keycode(scancode)
.or_else(|| {
check_function_keys(&get_characters(event, true))
})
})
}
extern fn key_down(this: &Object, _sel: Sel, event: id) { extern fn key_down(this: &Object, _sel: Sel, event: id) {
//println!("keyDown"); //println!("keyDown");
unsafe { unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState); let state = &mut *(state_ptr as *mut ViewState);
let window_id = WindowId(get_window_id(state.window)); let window_id = WindowId(get_window_id(state.window));
let characters = get_characters(event, false);
state.raw_characters = get_characters(event); state.raw_characters = Some(characters.clone());
let keycode: c_ushort = msg_send![event, keyCode]; let scancode = get_scancode(event) as u32;
// We are checking here for F21-F24 keys, since their keycode let virtual_keycode = retrieve_keycode(event);
// 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]; let is_repeat = msg_send![event, isARepeat];
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
@@ -436,17 +469,6 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
}, },
}; };
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);
state.raw_characters = {
Some(string.to_owned())
};
if let Some(shared) = state.shared.upgrade() { if let Some(shared) = state.shared.upgrade() {
shared.pending_events shared.pending_events
.lock() .lock()
@@ -454,7 +476,7 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
.push_back(window_event); .push_back(window_event);
// Emit `ReceivedCharacter` for key repeats // Emit `ReceivedCharacter` for key repeats
if is_repeat && state.is_key_down{ if is_repeat && state.is_key_down{
for character in string.chars() { for character in characters.chars() {
let window_event = Event::WindowEvent { let window_event = Event::WindowEvent {
window_id, window_id,
event: WindowEvent::ReceivedCharacter(character), event: WindowEvent::ReceivedCharacter(character),
@@ -483,16 +505,9 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
state.is_key_down = false; state.is_key_down = false;
// We need characters here to check for additional keys such as let scancode = get_scancode(event) as u32;
// F21-F24. let virtual_keycode = retrieve_keycode(event);
let characters = get_characters(event);
let keycode: c_ushort = msg_send![event, 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 { let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.window)), window_id: WindowId(get_window_id(state.window)),
event: WindowEvent::KeyboardInput { event: WindowEvent::KeyboardInput {

View File

@@ -618,6 +618,11 @@ impl WindowExt for Window2 {
} }
} }
#[inline]
fn get_simple_fullscreen(&self) -> bool {
self.delegate.state.is_simple_fullscreen.get()
}
#[inline] #[inline]
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool { fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
let state = &self.delegate.state; let state = &self.delegate.state;
@@ -1137,6 +1142,14 @@ impl Window2 {
self.delegate.state.perform_maximized(maximized) self.delegate.state.perform_maximized(maximized)
} }
#[inline]
pub fn get_fullscreen(&self) -> Option<RootMonitorId> {
let state = &self.delegate.state;
let win_attribs = state.win_attribs.borrow();
win_attribs.fullscreen.clone()
}
#[inline] #[inline]
/// TODO: Right now set_fullscreen do not work on switching monitors /// TODO: Right now set_fullscreen do not work on switching monitors
/// in fullscreen mode /// in fullscreen mode

View File

@@ -1,33 +1,87 @@
use std::char; use std::{char, ptr};
use std::os::raw::c_int; use std::os::raw::c_int;
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use events::VirtualKeyCode; use events::VirtualKeyCode;
use events::ModifiersState; use events::ModifiersState;
use winapi::shared::minwindef::{WPARAM, LPARAM, UINT}; use winapi::shared::minwindef::{WPARAM, LPARAM, UINT, HKL, HKL__};
use winapi::um::winuser; use winapi::um::winuser;
use ScanCode; use ScanCode;
fn key_pressed(vkey: c_int) -> bool {
unsafe {
(winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15)
}
}
pub fn get_key_mods() -> ModifiersState { pub fn get_key_mods() -> ModifiersState {
let mut mods = ModifiersState::default(); let mut mods = ModifiersState::default();
unsafe { let filter_out_altgr = layout_uses_altgr() && key_pressed(winuser::VK_RMENU);
if winuser::GetKeyState(winuser::VK_SHIFT) & (1 << 15) == (1 << 15) {
mods.shift = true; mods.shift = key_pressed(winuser::VK_SHIFT);
} mods.ctrl = key_pressed(winuser::VK_CONTROL) && !filter_out_altgr;
if winuser::GetKeyState(winuser::VK_CONTROL) & (1 << 15) == (1 << 15) { mods.alt = key_pressed(winuser::VK_MENU) && !filter_out_altgr;
mods.ctrl = true; mods.logo = key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN);
}
if winuser::GetKeyState(winuser::VK_MENU) & (1 << 15) == (1 << 15) {
mods.alt = true;
}
if (winuser::GetKeyState(winuser::VK_LWIN) | winuser::GetKeyState(winuser::VK_RWIN)) & (1 << 15) == (1 << 15) {
mods.logo = true;
}
}
mods mods
} }
unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option<char> {
let mut unicode_bytes = [0u16; 5];
let len = winuser::ToUnicodeEx(v_key, 0, keyboard_state.as_ptr(), unicode_bytes.as_mut_ptr(), unicode_bytes.len() as _, 0, hkl);
if len >= 1 {
char::decode_utf16(unicode_bytes.into_iter().cloned()).next().and_then(|c| c.ok())
} else {
None
}
}
/// Figures out if the keyboard layout has an AltGr key instead of an Alt key.
///
/// Unfortunately, the Windows API doesn't give a way for us to conveniently figure that out. So,
/// we use a technique blatantly stolen from [the Firefox source code][source]: iterate over every
/// possible virtual key and compare the `char` output when AltGr is pressed vs when it isn't. If
/// pressing AltGr outputs characters that are different from the standard characters, the layout
/// uses AltGr. Otherwise, it doesn't.
///
/// [source]: https://github.com/mozilla/gecko-dev/blob/265e6721798a455604328ed5262f430cfcc37c2f/widget/windows/KeyboardLayout.cpp#L4356-L4416
fn layout_uses_altgr() -> bool {
unsafe {
static ACTIVE_LAYOUT: AtomicPtr<HKL__> = AtomicPtr::new(ptr::null_mut());
static USES_ALTGR: AtomicBool = AtomicBool::new(false);
let hkl = winuser::GetKeyboardLayout(0);
let old_hkl = ACTIVE_LAYOUT.swap(hkl, Ordering::SeqCst);
if hkl == old_hkl {
return USES_ALTGR.load(Ordering::SeqCst);
}
let mut keyboard_state_altgr = [0u8; 256];
// AltGr is an alias for Ctrl+Alt for... some reason. Whatever it is, those are the keypresses
// we have to emulate to do an AltGr test.
keyboard_state_altgr[winuser::VK_MENU as usize] = 0x80;
keyboard_state_altgr[winuser::VK_CONTROL as usize] = 0x80;
let keyboard_state_empty = [0u8; 256];
for v_key in 0..=255 {
let key_noaltgr = get_char(&keyboard_state_empty, v_key, hkl);
let key_altgr = get_char(&keyboard_state_altgr, v_key, hkl);
if let (Some(noaltgr), Some(altgr)) = (key_noaltgr, key_altgr) {
if noaltgr != altgr {
USES_ALTGR.store(true, Ordering::SeqCst);
return true;
}
}
}
USES_ALTGR.store(false, Ordering::SeqCst);
false
}
}
pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> { pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
// VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx // VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
match vkey { match vkey {

View File

@@ -711,6 +711,22 @@ unsafe fn callback_inner(
0 0
}, },
winuser::WM_MOUSEHWHEEL => {
use events::MouseScrollDelta::LineDelta;
use events::TouchPhase;
let value = (wparam >> 16) as i16;
let value = value as i32;
let value = value as f32 / winuser::WHEEL_DELTA as f32;
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(value, 0.0), phase: TouchPhase::Moved, modifiers: event::get_key_mods() },
});
0
},
winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => {
use events::ElementState::Pressed; use events::ElementState::Pressed;
use events::VirtualKeyCode; use events::VirtualKeyCode;
@@ -1036,22 +1052,12 @@ unsafe fn callback_inner(
} }
winuser::WM_SETFOCUS => { winuser::WM_SETFOCUS => {
use events::WindowEvent::{Focused, CursorMoved}; use events::WindowEvent::Focused;
send_event(Event::WindowEvent { send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)), window_id: SuperWindowId(WindowId(window)),
event: Focused(true) event: Focused(true)
}); });
let x = windowsx::GET_X_LPARAM(lparam) as f64;
let y = windowsx::GET_Y_LPARAM(lparam) as f64;
let dpi_factor = get_hwnd_scale_factor(window);
let position = LogicalPosition::from_physical((x, y), dpi_factor);
send_event(Event::WindowEvent {
window_id: SuperWindowId(WindowId(window)),
event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() },
});
0 0
}, },

View File

@@ -355,6 +355,12 @@ impl Window {
}); });
} }
#[inline]
pub fn get_fullscreen(&self) -> Option<RootMonitorId> {
let window_state = self.window_state.lock().unwrap();
window_state.fullscreen.clone()
}
#[inline] #[inline]
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) { pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
unsafe { unsafe {
@@ -625,9 +631,6 @@ unsafe fn init(
format!("{}", io::Error::last_os_error())))); format!("{}", io::Error::last_os_error()))));
} }
winuser::SetWindowLongW(handle, winuser::GWL_STYLE, 0);
winuser::SetWindowLongW(handle, winuser::GWL_EXSTYLE, 0);
WindowWrapper(handle) WindowWrapper(handle)
}; };

View File

@@ -369,6 +369,12 @@ impl Window {
self.window.set_fullscreen(monitor) self.window.set_fullscreen(monitor)
} }
/// Gets the window's current fullscreen state.
#[inline]
pub fn get_fullscreen(&self) -> Option<MonitorId> {
self.window.get_fullscreen()
}
/// Turn window decorations on or off. /// Turn window decorations on or off.
#[inline] #[inline]
pub fn set_decorations(&self, decorations: bool) { pub fn set_decorations(&self, decorations: bool) {
@@ -485,7 +491,7 @@ impl MonitorId {
/// ///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable. /// - **X11:** This respects Xft.dpi XResource, and can be overridden using the `WINIT_HIDPI_FACTOR` environment variable.
/// - **Android:** Always returns 1.0. /// - **Android:** Always returns 1.0.
#[inline] #[inline]
pub fn get_hidpi_factor(&self) -> f64 { pub fn get_hidpi_factor(&self) -> f64 {