diff --git a/CHANGELOG.md b/CHANGELOG.md index 57abee57da..2ace1cb69f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # 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 @@ -11,6 +23,10 @@ - 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, 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) diff --git a/Cargo.toml b/Cargo.toml index 2e8ddeed13..35fde6d689 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winit" -version = "0.18.1" +version = "0.19.1" authors = ["The winit contributors", "Pierre Krieger "] description = "Cross-platform window creation library." keywords = ["windowing"] diff --git a/README.md b/README.md index 0360e126dd..b79568af57 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,19 @@ ```toml [dependencies] -winit = "0.18.1" +winit = "0.19.1" ``` ## [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 Winit is a window creation and management library. It can create windows and lets you handle diff --git a/src/os/unix.rs b/src/os/unix.rs index 3a468bc2c1..f5e2f61c95 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -113,6 +113,13 @@ pub trait EventsLoopExt { #[doc(hidden)] fn get_xlib_xconnection(&self) -> Option>; + + /// 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 { @@ -152,6 +159,14 @@ impl EventsLoopExt for EventsLoop { fn get_xlib_xconnection(&self) -> Option> { 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. diff --git a/src/platform/emscripten/mod.rs b/src/platform/emscripten/mod.rs index 3a5feb61d6..a762682453 100644 --- a/src/platform/emscripten/mod.rs +++ b/src/platform/emscripten/mod.rs @@ -1,4 +1,4 @@ -#![cfg(target_os = "emscripten")] +#![cfg(any(target_os = "emscripten", target_arch = "wasm32"))] mod ffi; diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index ee483487cc..82275e634e 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -229,6 +229,10 @@ impl EventsLoop { pub fn get_available_monitors(&self) -> VecDeque { get_available_monitors(&self.env.outputs) } + + pub fn get_display(&self) -> &Display { + &*self.display + } } /* diff --git a/src/platform/linux/wayland/keyboard.rs b/src/platform/linux/wayland/keyboard.rs index 1b487120dc..a20724e0f9 100644 --- a/src/platform/linux/wayland/keyboard.rs +++ b/src/platform/linux/wayland/keyboard.rs @@ -312,6 +312,13 @@ fn keysym_to_vkey(keysym: u32) -> Option { keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma), keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter), 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::Period), // => Some(VirtualKeyCode::Playpause), diff --git a/src/platform/linux/x11/events.rs b/src/platform/linux/x11/events.rs index 037ee4c482..853064134a 100644 --- a/src/platform/linux/x11/events.rs +++ b/src/platform/linux/x11/events.rs @@ -82,11 +82,11 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option { ffi::XK_KP_Delete => events::VirtualKeyCode::Delete, ffi::XK_KP_Equal => events::VirtualKeyCode::NumpadEquals, //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_Subtract => events::VirtualKeyCode::NumpadSubtract, + ffi::XK_KP_Subtract => events::VirtualKeyCode::Subtract, //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_1 => events::VirtualKeyCode::Numpad1, ffi::XK_KP_2 => events::VirtualKeyCode::Numpad2, diff --git a/src/platform/linux/x11/mod.rs b/src/platform/linux/x11/mod.rs index 5596297921..0200c71815 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform/linux/x11/mod.rs @@ -19,7 +19,13 @@ use std::collections::HashMap; use std::ffi::CStr; use std::ops::Deref; use std::os::raw::*; -use libc::{select, fd_set, FD_SET, FD_ZERO, FD_ISSET, EINTR, EINVAL, ENOMEM, EBADF, __errno_location}; +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::atomic::{self, AtomicBool}; diff --git a/src/platform/linux/x11/monitor.rs b/src/platform/linux/x11/monitor.rs index d6309f637f..cf1f456c46 100644 --- a/src/platform/linux/x11/monitor.rs +++ b/src/platform/linux/x11/monitor.rs @@ -131,9 +131,14 @@ impl XConnection { fn query_monitor_list(&self) -> Vec { unsafe { let root = (self.xlib.XDefaultRootWindow)(self.display); - // WARNING: this function is supposedly very slow, on the order of hundreds of ms. - // Upon failure, `resources` will be null. - let resources = (self.xrandr.XRRGetScreenResources)(self.display, root); + 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. + // Upon failure, `resources` will be null. + (self.xrandr.XRRGetScreenResources)(self.display, root) + }; + if resources.is_null() { panic!("[winit] `XRRGetScreenResources` returned NULL. That should only happen if the root window doesn't exist."); } diff --git a/src/platform/linux/x11/util/randr.rs b/src/platform/linux/x11/util/randr.rs index e730469ba3..5054dbdb01 100644 --- a/src/platform/linux/x11/util/randr.rs +++ b/src/platform/linux/x11/util/randr.rs @@ -79,6 +79,24 @@ impl From<*mut ffi::XRRCrtcInfo> for MonitorRepr { } impl XConnection { + // Retrieve DPI from Xft.dpi property + pub unsafe fn get_xft_dpi(&self) -> Option { + (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( &self, resources: *mut ffi::XRRScreenResources, @@ -101,10 +119,15 @@ impl XConnection { (*output_info).nameLen as usize, ); let name = String::from_utf8_lossy(name_slice).into(); - let hidpi_factor = calc_dpi_factor( - repr.get_dimensions(), - ((*output_info).mm_width as u64, (*output_info).mm_height as u64), - ); + let hidpi_factor = if let Some(dpi) = self.get_xft_dpi() { + dpi / 96. + } else { + calc_dpi_factor( + repr.get_dimensions(), + ((*output_info).mm_width as u64, (*output_info).mm_height as u64), + ) + }; + (self.xrandr.XRRFreeOutputInfo)(output_info); Some((name, hidpi_factor)) } diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index 358987d619..74532a2747 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -660,8 +660,8 @@ pub fn scancode_to_keycode(code: c_ushort) -> Option { 0x33 => events::VirtualKeyCode::Back, //0x34 => unkown, 0x35 => events::VirtualKeyCode::Escape, - 0x36 => events::VirtualKeyCode::LWin, - 0x37 => events::VirtualKeyCode::RWin, + 0x36 => events::VirtualKeyCode::RWin, + 0x37 => events::VirtualKeyCode::LWin, 0x38 => events::VirtualKeyCode::LShift, //0x39 => Caps lock, 0x3a => events::VirtualKeyCode::LAlt, @@ -683,6 +683,7 @@ pub fn scancode_to_keycode(code: c_ushort) -> Option { 0x4a => events::VirtualKeyCode::VolumeDown, 0x4b => events::VirtualKeyCode::Divide, 0x4c => events::VirtualKeyCode::NumpadEnter, + 0x4e => events::VirtualKeyCode::Subtract, //0x4d => unkown, 0x4e => events::VirtualKeyCode::Subtract, 0x4f => events::VirtualKeyCode::F18, diff --git a/src/platform/mod.rs b/src/platform/mod.rs index be96878500..8885569d1d 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -15,12 +15,13 @@ mod platform; #[cfg(target_os = "ios")] #[path="ios/mod.rs"] mod platform; -#[cfg(target_os = "emscripten")] +#[cfg(any(target_os = "emscripten", target_arch = "wasm32"))] #[path="emscripten/mod.rs"] mod platform; #[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"), not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"), not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd"), - not(target_os = "emscripten")))] + not(target_os = "emscripten"), + not(target_arch = "wasm32")))] compile_error!("The platform you're compiling for is not supported by winit"); diff --git a/src/platform/windows/event.rs b/src/platform/windows/event.rs index dfdff6ee63..1546f8efe8 100644 --- a/src/platform/windows/event.rs +++ b/src/platform/windows/event.rs @@ -1,31 +1,85 @@ -use std::char; +use std::{char, ptr}; use std::os::raw::c_int; +use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; use events::VirtualKeyCode; use events::ModifiersState; -use winapi::shared::minwindef::{WPARAM, LPARAM, UINT}; +use winapi::shared::minwindef::{WPARAM, LPARAM, UINT, HKL, HKL__}; use winapi::um::winuser; use ScanCode; +fn key_pressed(vkey: c_int) -> bool { + unsafe { + (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) + } +} + pub fn get_key_mods() -> ModifiersState { let mut mods = ModifiersState::default(); + let filter_out_altgr = layout_uses_altgr() && key_pressed(winuser::VK_RMENU); + + mods.shift = key_pressed(winuser::VK_SHIFT); + mods.ctrl = key_pressed(winuser::VK_CONTROL) && !filter_out_altgr; + mods.alt = key_pressed(winuser::VK_MENU) && !filter_out_altgr; + mods.logo = key_pressed(winuser::VK_LWIN) || key_pressed(winuser::VK_RWIN); + mods +} + +unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option { + 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 { - if winuser::GetKeyState(winuser::VK_SHIFT) & (1 << 15) == (1 << 15) { - mods.shift = true; - } - if winuser::GetKeyState(winuser::VK_CONTROL) & (1 << 15) == (1 << 15) { - mods.ctrl = true; - } - if winuser::GetKeyState(winuser::VK_MENU) & (1 << 15) == (1 << 15) { - mods.alt = true; + static ACTIVE_LAYOUT: AtomicPtr = 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); } - if (winuser::GetKeyState(winuser::VK_LWIN) | winuser::GetKeyState(winuser::VK_RWIN)) & (1 << 15) == (1 << 15) { - mods.logo = true; + + 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 } - mods } pub fn vkey_to_winit_vkey(vkey: c_int) -> Option { diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index 252172e0ba..7bc96c912d 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -1052,22 +1052,12 @@ unsafe fn callback_inner( } winuser::WM_SETFOCUS => { - use events::WindowEvent::{Focused, CursorMoved}; + use events::WindowEvent::Focused; send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(window)), 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 }, diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index d15838934d..0d664d4d5a 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -625,9 +625,6 @@ unsafe fn init( format!("{}", io::Error::last_os_error())))); } - winuser::SetWindowLongW(handle, winuser::GWL_STYLE, 0); - winuser::SetWindowLongW(handle, winuser::GWL_EXSTYLE, 0); - WindowWrapper(handle) }; diff --git a/src/window.rs b/src/window.rs index cf36044880..1bc0dbf618 100644 --- a/src/window.rs +++ b/src/window.rs @@ -485,7 +485,7 @@ impl MonitorId { /// /// ## 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. #[inline] pub fn get_hidpi_factor(&self) -> f64 {