Compare commits

...

16 Commits

Author SHA1 Message Date
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
18 changed files with 409 additions and 103 deletions

View File

@@ -1,12 +1,32 @@
# Unreleased # Unreleased
# 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,6 +1,6 @@
[package] [package]
name = "winit" name = "winit"
version = "0.18.1" version = "0.19.1"
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"]

View File

@@ -7,11 +7,19 @@
```toml ```toml
[dependencies] [dependencies]
winit = "0.18.1" winit = "0.19.1"
``` ```
## [Documentation](https://docs.rs/winit) ## [Documentation](https://docs.rs/winit)
## 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

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

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

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

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);
let resources = if version_is_at_least(1, 3) {
(self.xrandr.XRRGetScreenResourcesCurrent)(self.display, root)
} else {
// WARNING: this function is supposedly very slow, on the order of hundreds of ms. // WARNING: this function is supposedly very slow, on the order of hundreds of ms.
// Upon failure, `resources` will be null. // Upon failure, `resources` will be null.
let resources = (self.xrandr.XRRGetScreenResources)(self.display, root); (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

@@ -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() {
dpi / 96.
} else {
calc_dpi_factor(
repr.get_dimensions(), repr.get_dimensions(),
((*output_info).mm_width as u64, (*output_info).mm_height as u64), ((*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

@@ -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,10 +741,9 @@ 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,
@@ -693,7 +753,7 @@ pub fn check_additional_virtual_key_codes(
_ => 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

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

@@ -625,9 +625,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

@@ -485,7 +485,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 {