mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-25 23:01:30 +11:00
Merge branch 'master' of https://github.com/tomaka/winit into xim-send-spot
This commit is contained in:
commit
f2ee78bcf5
15
Cargo.toml
15
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "winit"
|
name = "winit"
|
||||||
version = "0.6.4"
|
version = "0.7.2"
|
||||||
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"]
|
||||||
|
@ -23,10 +23,9 @@ objc = "0.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
objc = "0.2"
|
objc = "0.2"
|
||||||
cgl = "0.2"
|
cocoa = "0.9"
|
||||||
cocoa = "=0.5.2"
|
core-foundation = "0.4"
|
||||||
core-foundation = "0.2"
|
core-graphics = "0.8"
|
||||||
core-graphics = "0.4"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi = "0.2"
|
winapi = "0.2"
|
||||||
|
@ -37,8 +36,8 @@ kernel32-sys = "0.2"
|
||||||
dwmapi-sys = "0.1"
|
dwmapi-sys = "0.1"
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||||
wayland-client = { version = "0.8.6", features = ["dlopen"] }
|
wayland-client = { version = "0.9.9", features = ["dlopen"] }
|
||||||
wayland-kbd = "0.8.0"
|
wayland-kbd = "0.9.1"
|
||||||
wayland-window = "0.5.0"
|
wayland-window = "0.6.1"
|
||||||
tempfile = "2.1"
|
tempfile = "2.1"
|
||||||
x11-dl = "2.8"
|
x11-dl = "2.8"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
winit = "0.6"
|
winit = "0.7"
|
||||||
```
|
```
|
||||||
|
|
||||||
## [Documentation](https://docs.rs/winit)
|
## [Documentation](https://docs.rs/winit)
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -104,8 +104,6 @@ extern crate dwmapi;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate objc;
|
extern crate objc;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
extern crate cgl;
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
extern crate cocoa;
|
extern crate cocoa;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
extern crate core_foundation;
|
extern crate core_foundation;
|
||||||
|
@ -114,7 +112,7 @@ extern crate core_graphics;
|
||||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||||
extern crate x11_dl;
|
extern crate x11_dl;
|
||||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
||||||
#[macro_use(wayland_env,declare_handler)]
|
#[macro_use]
|
||||||
extern crate wayland_client;
|
extern crate wayland_client;
|
||||||
|
|
||||||
pub use events::*;
|
pub use events::*;
|
||||||
|
@ -181,6 +179,14 @@ pub struct ButtonId(u32);
|
||||||
/// Provides a way to retreive events from the windows that were registered to it.
|
/// Provides a way to retreive events from the windows that were registered to it.
|
||||||
///
|
///
|
||||||
/// To wake up an `EventsLoop` from a another thread, see the `EventsLoopProxy` docs.
|
/// To wake up an `EventsLoop` from a another thread, see the `EventsLoopProxy` docs.
|
||||||
|
///
|
||||||
|
/// Usage will result in display backend initialisation, this can be controlled on linux
|
||||||
|
/// using an environment variable `WINIT_UNIX_BACKEND`.
|
||||||
|
/// > Legal values are `x11` and `wayland`. If this variable is set only the named backend
|
||||||
|
/// > will be tried by winit. If it is not set, winit will try to connect to a wayland connection,
|
||||||
|
/// > and if it fails will fallback on x11.
|
||||||
|
/// >
|
||||||
|
/// > If this variable is set with any other value, winit will panic.
|
||||||
pub struct EventsLoop {
|
pub struct EventsLoop {
|
||||||
events_loop: platform::EventsLoop,
|
events_loop: platform::EventsLoop,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,6 @@ use WindowBuilder;
|
||||||
use platform::x11::XConnection;
|
use platform::x11::XConnection;
|
||||||
use platform::x11::ffi::XVisualInfo;
|
use platform::x11::ffi::XVisualInfo;
|
||||||
|
|
||||||
use wayland_client::protocol::wl_display::WlDisplay;
|
|
||||||
use wayland_client::protocol::wl_surface::WlSurface;
|
|
||||||
|
|
||||||
pub use platform::x11;
|
pub use platform::x11;
|
||||||
|
|
||||||
// TODO: do not expose XConnection
|
// TODO: do not expose XConnection
|
||||||
|
@ -65,24 +62,6 @@ pub trait WindowExt {
|
||||||
///
|
///
|
||||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||||
fn get_wayland_display(&self) -> Option<*mut libc::c_void>;
|
fn get_wayland_display(&self) -> Option<*mut libc::c_void>;
|
||||||
|
|
||||||
/// Returns a reference to the `WlSurface` object of wayland that is used by this window.
|
|
||||||
///
|
|
||||||
/// For use with the `wayland-client` crate.
|
|
||||||
///
|
|
||||||
/// **This function is not part of winit's public API.**
|
|
||||||
///
|
|
||||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
|
||||||
fn get_wayland_client_surface(&self) -> Option<&WlSurface>;
|
|
||||||
|
|
||||||
/// Returns a pointer to the `WlDisplay` object of wayland that is used by this window.
|
|
||||||
///
|
|
||||||
/// For use with the `wayland-client` crate.
|
|
||||||
///
|
|
||||||
/// **This function is not part of winit's public API.**
|
|
||||||
///
|
|
||||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
|
||||||
fn get_wayland_client_display(&self) -> Option<&WlDisplay>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowExt for Window {
|
impl WindowExt for Window {
|
||||||
|
@ -132,28 +111,17 @@ impl WindowExt for Window {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_wayland_surface(&self) -> Option<*mut libc::c_void> {
|
fn get_wayland_surface(&self) -> Option<*mut libc::c_void> {
|
||||||
use wayland_client::Proxy;
|
use wayland_client::Proxy;
|
||||||
self.get_wayland_client_surface().map(|p| p.ptr() as *mut _)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_wayland_display(&self) -> Option<*mut libc::c_void> {
|
|
||||||
use wayland_client::Proxy;
|
|
||||||
self.get_wayland_client_display().map(|p| p.ptr() as *mut _)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn get_wayland_client_surface(&self) -> Option<&WlSurface> {
|
|
||||||
match self.window {
|
match self.window {
|
||||||
LinuxWindow::Wayland(ref w) => Some(w.get_surface()),
|
LinuxWindow::Wayland(ref w) => Some(w.get_surface().ptr() as *mut _),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_wayland_client_display(&self) -> Option<&WlDisplay> {
|
fn get_wayland_display(&self) -> Option<*mut libc::c_void> {
|
||||||
|
use wayland_client::Proxy;
|
||||||
match self.window {
|
match self.window {
|
||||||
LinuxWindow::Wayland(ref w) => Some(w.get_display()),
|
LinuxWindow::Wayland(ref w) => Some(w.get_display().ptr() as *mut _),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub trait WindowExt {
|
||||||
impl WindowExt for Window {
|
impl WindowExt for Window {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_hwnd(&self) -> *mut libc::c_void {
|
fn get_hwnd(&self) -> *mut libc::c_void {
|
||||||
self.window.platform_window()
|
self.window.hwnd() as *mut _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,7 @@ impl<'a> Iterator for PollEventsIterator<'a> {
|
||||||
},
|
},
|
||||||
location: (motion.x as f64, motion.y as f64),
|
location: (motion.x as f64, motion.y as f64),
|
||||||
id: motion.pointer_id as u64,
|
id: motion.pointer_id as u64,
|
||||||
|
device_id: DEVICE_ID,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
Ok(android_glue::Event::InitWindow) => {
|
Ok(android_glue::Event::InitWindow) => {
|
||||||
|
@ -273,3 +274,6 @@ impl WindowProxy {
|
||||||
android_glue::wake_event_loop();
|
android_glue::wake_event_loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||||
|
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||||
|
|
|
@ -66,7 +66,7 @@ extern {
|
||||||
pub fn longjmp(env: *mut libc::c_void, val: libc::c_int);
|
pub fn longjmp(env: *mut libc::c_void, val: libc::c_int);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NSString {
|
pub trait NSString: Sized {
|
||||||
unsafe fn alloc(_: Self) -> id {
|
unsafe fn alloc(_: Self) -> id {
|
||||||
msg_send![class("NSString"), alloc]
|
msg_send![class("NSString"), alloc]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow};
|
use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow};
|
||||||
use libc;
|
use libc;
|
||||||
|
@ -15,6 +16,15 @@ mod dlopen;
|
||||||
pub mod wayland;
|
pub mod wayland;
|
||||||
pub mod x11;
|
pub mod x11;
|
||||||
|
|
||||||
|
/// Environment variable specifying which backend should be used on unix platform.
|
||||||
|
///
|
||||||
|
/// Legal values are x11 and wayland. If this variable is set only the named backend
|
||||||
|
/// will be tried by winit. If it is not set, winit will try to connect to a wayland connection,
|
||||||
|
/// and if it fails will fallback on x11.
|
||||||
|
///
|
||||||
|
/// If this variable is set with any other value, winit will panic.
|
||||||
|
const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
pub visual_infos: Option<XVisualInfo>,
|
pub visual_infos: Option<XVisualInfo>,
|
||||||
|
@ -24,19 +34,42 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
pub enum UnixBackend {
|
pub enum UnixBackend {
|
||||||
X(Arc<XConnection>),
|
X(Arc<XConnection>),
|
||||||
Wayland(Arc<wayland::WaylandContext>),
|
Wayland(Arc<wayland::WaylandContext>),
|
||||||
Error(XNotSupported),
|
Error(Option<XNotSupported>, Option<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static!(
|
lazy_static!(
|
||||||
pub static ref UNIX_BACKEND: UnixBackend = {
|
pub static ref UNIX_BACKEND: UnixBackend = {
|
||||||
if let Some(ctxt) = wayland::WaylandContext::init() {
|
#[inline]
|
||||||
UnixBackend::Wayland(Arc::new(ctxt))
|
fn x_backend() -> Result<UnixBackend, XNotSupported> {
|
||||||
} else {
|
|
||||||
match XConnection::new(Some(x_error_callback)) {
|
match XConnection::new(Some(x_error_callback)) {
|
||||||
Ok(x) => UnixBackend::X(Arc::new(x)),
|
Ok(x) => Ok(UnixBackend::X(Arc::new(x))),
|
||||||
Err(e) => UnixBackend::Error(e),
|
Err(e) => Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
fn wayland_backend() -> Result<UnixBackend, ()> {
|
||||||
|
wayland::WaylandContext::init()
|
||||||
|
.map(|ctx| UnixBackend::Wayland(Arc::new(ctx)))
|
||||||
|
.ok_or(())
|
||||||
|
}
|
||||||
|
match env::var(BACKEND_PREFERENCE_ENV_VAR) {
|
||||||
|
Ok(s) => match s.as_str() {
|
||||||
|
"x11" => x_backend().unwrap_or_else(|e| UnixBackend::Error(Some(e), None)),
|
||||||
|
"wayland" => wayland_backend().unwrap_or_else(|_| {
|
||||||
|
UnixBackend::Error(None, Some("Wayland not available".into()))
|
||||||
|
}),
|
||||||
|
_ => panic!("Unknown environment variable value for {}, try one of `x11`,`wayland`",
|
||||||
|
BACKEND_PREFERENCE_ENV_VAR),
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
// Try wayland, fallback to X11
|
||||||
|
wayland_backend().unwrap_or_else(|_| {
|
||||||
|
x_backend().unwrap_or_else(|x_err| {
|
||||||
|
UnixBackend::Error(Some(x_err), Some("Wayland not available".into()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -85,7 +118,7 @@ pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(MonitorId::X)
|
.map(MonitorId::X)
|
||||||
.collect(),
|
.collect(),
|
||||||
UnixBackend::Error(_) => { let mut d = VecDeque::new(); d.push_back(MonitorId::None); d},
|
UnixBackend::Error(..) => { let mut d = VecDeque::new(); d.push_back(MonitorId::None); d},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +127,7 @@ pub fn get_primary_monitor() -> MonitorId {
|
||||||
match *UNIX_BACKEND {
|
match *UNIX_BACKEND {
|
||||||
UnixBackend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)),
|
UnixBackend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)),
|
||||||
UnixBackend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)),
|
UnixBackend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)),
|
||||||
UnixBackend::Error(_) => MonitorId::None,
|
UnixBackend::Error(..) => MonitorId::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +180,7 @@ impl Window2 {
|
||||||
UnixBackend::X(_) => {
|
UnixBackend::X(_) => {
|
||||||
x11::Window2::new(events_loop, window, pl_attribs).map(Window2::X)
|
x11::Window2::new(events_loop, window, pl_attribs).map(Window2::X)
|
||||||
},
|
},
|
||||||
UnixBackend::Error(_) => {
|
UnixBackend::Error(..) => {
|
||||||
// If the Backend is Error(), it is not possible to instanciate an EventsLoop at all,
|
// If the Backend is Error(), it is not possible to instanciate an EventsLoop at all,
|
||||||
// thus this function cannot be called!
|
// thus this function cannot be called!
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
@ -324,7 +357,7 @@ impl EventsLoop {
|
||||||
EventsLoop::X(x11::EventsLoop::new(ctxt.clone()))
|
EventsLoop::X(x11::EventsLoop::new(ctxt.clone()))
|
||||||
},
|
},
|
||||||
|
|
||||||
UnixBackend::Error(_) => {
|
UnixBackend::Error(..) => {
|
||||||
panic!("Attempted to create an EventsLoop while no backend was available.")
|
panic!("Attempted to create an EventsLoop while no backend was available.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::sync::atomic::{self, AtomicBool};
|
||||||
use super::{DecoratedHandler, WindowId, DeviceId, WaylandContext};
|
use super::{DecoratedHandler, WindowId, DeviceId, WaylandContext};
|
||||||
|
|
||||||
|
|
||||||
use wayland_client::{EventQueue, EventQueueHandle, Init, Proxy};
|
use wayland_client::{EventQueue, EventQueueHandle, Init, Proxy, Liveness};
|
||||||
use wayland_client::protocol::{wl_seat, wl_surface, wl_pointer, wl_keyboard};
|
use wayland_client::protocol::{wl_seat, wl_surface, wl_pointer, wl_keyboard};
|
||||||
|
|
||||||
use super::make_wid;
|
use super::make_wid;
|
||||||
|
@ -159,13 +159,13 @@ impl EventsLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prune_dead_windows(&self) {
|
fn prune_dead_windows(&self) {
|
||||||
self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.is_alive());
|
self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.status() == Liveness::Alive);
|
||||||
let mut evq_guard = self.evq.lock().unwrap();
|
let mut evq_guard = self.evq.lock().unwrap();
|
||||||
let mut state = evq_guard.state();
|
let mut state = evq_guard.state();
|
||||||
let handler = state.get_mut_handler::<InputHandler>(self.hid);
|
let handler = state.get_mut_handler::<InputHandler>(self.hid);
|
||||||
handler.windows.retain(|w| w.is_alive());
|
handler.windows.retain(|w| w.status() == Liveness::Alive);
|
||||||
if let Some(w) = handler.mouse_focus.take() {
|
if let Some(w) = handler.mouse_focus.take() {
|
||||||
if w.is_alive() {
|
if w.status() == Liveness::Alive {
|
||||||
handler.mouse_focus = Some(w)
|
handler.mouse_focus = Some(w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -338,21 +338,17 @@ impl Window {
|
||||||
|
|
||||||
let masks = if screen.is_some() {
|
let masks = if screen.is_some() {
|
||||||
// Fullscreen window
|
// Fullscreen window
|
||||||
appkit::NSBorderlessWindowMask as NSUInteger |
|
appkit::NSBorderlessWindowMask | appkit::NSResizableWindowMask |
|
||||||
appkit::NSResizableWindowMask as NSUInteger |
|
appkit::NSTitledWindowMask
|
||||||
appkit::NSTitledWindowMask as NSUInteger
|
|
||||||
} else if attrs.decorations {
|
} else if attrs.decorations {
|
||||||
// Window with a titlebar
|
// Window with a titlebar
|
||||||
appkit::NSClosableWindowMask as NSUInteger |
|
appkit::NSClosableWindowMask | appkit::NSMiniaturizableWindowMask |
|
||||||
appkit::NSMiniaturizableWindowMask as NSUInteger |
|
appkit::NSResizableWindowMask | appkit::NSTitledWindowMask
|
||||||
appkit::NSResizableWindowMask as NSUInteger |
|
|
||||||
appkit::NSTitledWindowMask as NSUInteger
|
|
||||||
} else {
|
} else {
|
||||||
// Window without a titlebar
|
// Window without a titlebar
|
||||||
appkit::NSClosableWindowMask as NSUInteger |
|
appkit::NSClosableWindowMask | appkit::NSMiniaturizableWindowMask |
|
||||||
appkit::NSMiniaturizableWindowMask as NSUInteger |
|
appkit::NSResizableWindowMask |
|
||||||
appkit::NSResizableWindowMask as NSUInteger |
|
appkit::NSFullSizeContentViewWindowMask
|
||||||
appkit::NSFullSizeContentViewWindowMask as NSUInteger
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
|
let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
|
||||||
|
|
|
@ -1,426 +0,0 @@
|
||||||
use std::mem;
|
|
||||||
use std::ptr;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::sync::mpsc::Sender;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::os::windows::ffi::OsStringExt;
|
|
||||||
|
|
||||||
use CursorState;
|
|
||||||
use WindowEvent as Event;
|
|
||||||
use KeyboardInput;
|
|
||||||
use events::ModifiersState;
|
|
||||||
use super::event;
|
|
||||||
use super::WindowState;
|
|
||||||
|
|
||||||
use user32;
|
|
||||||
use shell32;
|
|
||||||
use winapi;
|
|
||||||
|
|
||||||
/// There's no parameters passed to the callback function, so it needs to get
|
|
||||||
/// its context (the HWND, the Sender for events, etc.) stashed in
|
|
||||||
/// a thread-local variable.
|
|
||||||
thread_local!(pub static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None));
|
|
||||||
|
|
||||||
pub struct ThreadLocalData {
|
|
||||||
pub win: winapi::HWND,
|
|
||||||
pub sender: Sender<Event>,
|
|
||||||
pub window_state: Arc<Mutex<WindowState>>,
|
|
||||||
pub mouse_in_window: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Equivalent to the windows api [MINMAXINFO](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632605%28v=vs.85%29.aspx)
|
|
||||||
/// struct. Used because winapi-rs doesn't have this declared.
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct MinMaxInfo {
|
|
||||||
reserved: winapi::POINT, // Do not use/change
|
|
||||||
max_size: winapi::POINT,
|
|
||||||
max_position: winapi::POINT,
|
|
||||||
min_track: winapi::POINT,
|
|
||||||
max_track: winapi::POINT
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that the window is the good one, and if so send the event to it.
|
|
||||||
fn send_event(input_window: winapi::HWND, event: Event) {
|
|
||||||
CONTEXT_STASH.with(|context_stash| {
|
|
||||||
let context_stash = context_stash.borrow();
|
|
||||||
let stored = match *context_stash {
|
|
||||||
None => return,
|
|
||||||
Some(ref v) => v
|
|
||||||
};
|
|
||||||
|
|
||||||
let &ThreadLocalData { ref win, ref sender, .. } = stored;
|
|
||||||
|
|
||||||
if win != &input_window {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.send(event).ok(); // ignoring if closed
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is the callback that is called by `DispatchMessage` in the events loop.
|
|
||||||
///
|
|
||||||
/// Returning 0 tells the Win32 API that the message has been processed.
|
|
||||||
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
|
|
||||||
pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|
||||||
wparam: winapi::WPARAM, lparam: winapi::LPARAM)
|
|
||||||
-> winapi::LRESULT
|
|
||||||
{
|
|
||||||
match msg {
|
|
||||||
winapi::WM_DESTROY => {
|
|
||||||
use events::WindowEvent::Closed;
|
|
||||||
|
|
||||||
CONTEXT_STASH.with(|context_stash| {
|
|
||||||
let context_stash = context_stash.borrow();
|
|
||||||
let stored = match *context_stash {
|
|
||||||
None => return,
|
|
||||||
Some(ref v) => v
|
|
||||||
};
|
|
||||||
|
|
||||||
let &ThreadLocalData { ref win, .. } = stored;
|
|
||||||
|
|
||||||
if win == &window {
|
|
||||||
user32::PostQuitMessage(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
send_event(window, Closed);
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_ERASEBKGND => {
|
|
||||||
1
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_SIZE => {
|
|
||||||
use events::WindowEvent::Resized;
|
|
||||||
let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
|
|
||||||
let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
|
|
||||||
send_event(window, Resized(w, h));
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_MOVE => {
|
|
||||||
use events::WindowEvent::Moved;
|
|
||||||
let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
|
|
||||||
let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
|
|
||||||
send_event(window, Moved(x, y));
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_CHAR => {
|
|
||||||
use std::mem;
|
|
||||||
use events::WindowEvent::ReceivedCharacter;
|
|
||||||
let chr: char = mem::transmute(wparam as u32);
|
|
||||||
send_event(window, ReceivedCharacter(chr));
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
// Prevents default windows menu hotkeys playing unwanted
|
|
||||||
// "ding" sounds. Alternatively could check for WM_SYSCOMMAND
|
|
||||||
// with wparam being SC_KEYMENU, but this may prevent some
|
|
||||||
// other unwanted default hotkeys as well.
|
|
||||||
winapi::WM_SYSCHAR => {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
winapi::WM_MOUSEMOVE => {
|
|
||||||
use events::WindowEvent::{MouseEntered, MouseMoved};
|
|
||||||
let mouse_outside_window = CONTEXT_STASH.with(|context_stash| {
|
|
||||||
let mut context_stash = context_stash.borrow_mut();
|
|
||||||
if let Some(context_stash) = context_stash.as_mut() {
|
|
||||||
if !context_stash.mouse_in_window {
|
|
||||||
context_stash.mouse_in_window = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
});
|
|
||||||
|
|
||||||
if mouse_outside_window {
|
|
||||||
send_event(window, MouseEntered { device_id: DEVICE_ID });
|
|
||||||
|
|
||||||
// Calling TrackMouseEvent in order to receive mouse leave events.
|
|
||||||
user32::TrackMouseEvent(&mut winapi::TRACKMOUSEEVENT {
|
|
||||||
cbSize: mem::size_of::<winapi::TRACKMOUSEEVENT>() as winapi::DWORD,
|
|
||||||
dwFlags: winapi::TME_LEAVE,
|
|
||||||
hwndTrack: window,
|
|
||||||
dwHoverTime: winapi::HOVER_DEFAULT,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let x = winapi::GET_X_LPARAM(lparam) as f64;
|
|
||||||
let y = winapi::GET_Y_LPARAM(lparam) as f64;
|
|
||||||
|
|
||||||
send_event(window, MouseMoved { device_id: DEVICE_ID, position: (x, y) });
|
|
||||||
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_MOUSELEAVE => {
|
|
||||||
use events::WindowEvent::MouseLeft;
|
|
||||||
let mouse_in_window = CONTEXT_STASH.with(|context_stash| {
|
|
||||||
let mut context_stash = context_stash.borrow_mut();
|
|
||||||
if let Some(context_stash) = context_stash.as_mut() {
|
|
||||||
if context_stash.mouse_in_window {
|
|
||||||
context_stash.mouse_in_window = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
});
|
|
||||||
|
|
||||||
if mouse_in_window {
|
|
||||||
send_event(window, MouseLeft { device_id: DEVICE_ID });
|
|
||||||
}
|
|
||||||
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_MOUSEWHEEL => {
|
|
||||||
use events::WindowEvent::MouseWheel;
|
|
||||||
use events::MouseScrollDelta::LineDelta;
|
|
||||||
use events::TouchPhase;
|
|
||||||
|
|
||||||
let value = (wparam >> 16) as i16;
|
|
||||||
let value = value as i32;
|
|
||||||
let value = value as f32 / winapi::WHEEL_DELTA as f32;
|
|
||||||
|
|
||||||
send_event(window, MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved });
|
|
||||||
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_KEYDOWN | winapi::WM_SYSKEYDOWN => {
|
|
||||||
use events::ElementState::Pressed;
|
|
||||||
if msg == winapi::WM_SYSKEYDOWN && wparam as i32 == winapi::VK_F4 {
|
|
||||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
|
||||||
} else {
|
|
||||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
|
||||||
send_event(window, Event::KeyboardInput {
|
|
||||||
device_id: DEVICE_ID,
|
|
||||||
input: KeyboardInput {
|
|
||||||
state: Pressed,
|
|
||||||
scancode: scancode,
|
|
||||||
virtual_keycode: vkey,
|
|
||||||
modifiers: event::get_key_mods(),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_KEYUP | winapi::WM_SYSKEYUP => {
|
|
||||||
use events::ElementState::Released;
|
|
||||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
|
||||||
send_event(window, Event::KeyboardInput {
|
|
||||||
device_id: DEVICE_ID,
|
|
||||||
input: KeyboardInput {
|
|
||||||
state: Released,
|
|
||||||
scancode: scancode,
|
|
||||||
virtual_keycode: vkey,
|
|
||||||
modifiers: event::get_key_mods(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_LBUTTONDOWN => {
|
|
||||||
use events::WindowEvent::MouseInput;
|
|
||||||
use events::MouseButton::Left;
|
|
||||||
use events::ElementState::Pressed;
|
|
||||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left });
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_LBUTTONUP => {
|
|
||||||
use events::WindowEvent::MouseInput;
|
|
||||||
use events::MouseButton::Left;
|
|
||||||
use events::ElementState::Released;
|
|
||||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Released, button: Left });
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_RBUTTONDOWN => {
|
|
||||||
use events::WindowEvent::MouseInput;
|
|
||||||
use events::MouseButton::Right;
|
|
||||||
use events::ElementState::Pressed;
|
|
||||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right });
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_RBUTTONUP => {
|
|
||||||
use events::WindowEvent::MouseInput;
|
|
||||||
use events::MouseButton::Right;
|
|
||||||
use events::ElementState::Released;
|
|
||||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Released, button: Right });
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_MBUTTONDOWN => {
|
|
||||||
use events::WindowEvent::MouseInput;
|
|
||||||
use events::MouseButton::Middle;
|
|
||||||
use events::ElementState::Pressed;
|
|
||||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle });
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_MBUTTONUP => {
|
|
||||||
use events::WindowEvent::MouseInput;
|
|
||||||
use events::MouseButton::Middle;
|
|
||||||
use events::ElementState::Released;
|
|
||||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Released, button: Middle });
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_XBUTTONDOWN => {
|
|
||||||
use events::WindowEvent::MouseInput;
|
|
||||||
use events::MouseButton::Other;
|
|
||||||
use events::ElementState::Pressed;
|
|
||||||
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; // waiting on PR for winapi to add GET_XBUTTON_WPARAM
|
|
||||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8) });
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_XBUTTONUP => {
|
|
||||||
use events::WindowEvent::MouseInput;
|
|
||||||
use events::MouseButton::Other;
|
|
||||||
use events::ElementState::Released;
|
|
||||||
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
|
|
||||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8) });
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_INPUT => {
|
|
||||||
let mut data: winapi::RAWINPUT = mem::uninitialized();
|
|
||||||
let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT;
|
|
||||||
user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT,
|
|
||||||
mem::transmute(&mut data), &mut data_size,
|
|
||||||
mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT);
|
|
||||||
|
|
||||||
if data.header.dwType == winapi::RIM_TYPEMOUSE {
|
|
||||||
let _x = data.mouse.lLastX; // FIXME: this is not always the relative movement
|
|
||||||
let _y = data.mouse.lLastY;
|
|
||||||
// TODO:
|
|
||||||
//send_event(window, Event::MouseRawMovement { x: x, y: y });
|
|
||||||
|
|
||||||
0
|
|
||||||
|
|
||||||
} else {
|
|
||||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_SETFOCUS => {
|
|
||||||
use events::WindowEvent::Focused;
|
|
||||||
send_event(window, Focused(true));
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_KILLFOCUS => {
|
|
||||||
use events::WindowEvent::Focused;
|
|
||||||
send_event(window, Focused(false));
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_SETCURSOR => {
|
|
||||||
let call_def_window_proc = CONTEXT_STASH.with(|context_stash| {
|
|
||||||
let cstash = context_stash.borrow();
|
|
||||||
let mut call_def_window_proc = false;
|
|
||||||
if let Some(cstash) = cstash.as_ref() {
|
|
||||||
if let Ok(window_state) = cstash.window_state.lock() {
|
|
||||||
if cstash.mouse_in_window {
|
|
||||||
match window_state.cursor_state {
|
|
||||||
CursorState::Normal => {
|
|
||||||
user32::SetCursor(user32::LoadCursorW(
|
|
||||||
ptr::null_mut(),
|
|
||||||
window_state.cursor));
|
|
||||||
},
|
|
||||||
CursorState::Grab | CursorState::Hide => {
|
|
||||||
user32::SetCursor(ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
call_def_window_proc = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
call_def_window_proc
|
|
||||||
});
|
|
||||||
|
|
||||||
if call_def_window_proc {
|
|
||||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_DROPFILES => {
|
|
||||||
use events::WindowEvent::DroppedFile;
|
|
||||||
|
|
||||||
let hdrop = wparam as winapi::HDROP;
|
|
||||||
let mut pathbuf: [u16; winapi::MAX_PATH] = mem::uninitialized();
|
|
||||||
let num_drops = shell32::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
|
||||||
|
|
||||||
for i in 0..num_drops {
|
|
||||||
let nch = shell32::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
|
|
||||||
winapi::MAX_PATH as u32) as usize;
|
|
||||||
if nch > 0 {
|
|
||||||
send_event(window, DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shell32::DragFinish(hdrop);
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
winapi::WM_GETMINMAXINFO => {
|
|
||||||
let mmi = lparam as *mut MinMaxInfo;
|
|
||||||
//(*mmi).max_position = winapi::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor.
|
|
||||||
//(*mmi).max_size = winapi::POINT { x: .., y: .. }; // The dimensions of the primary monitor.
|
|
||||||
|
|
||||||
CONTEXT_STASH.with(|context_stash| {
|
|
||||||
match context_stash.borrow().as_ref() {
|
|
||||||
Some(cstash) => {
|
|
||||||
let window_state = cstash.window_state.lock().unwrap();
|
|
||||||
|
|
||||||
match window_state.attributes.min_dimensions {
|
|
||||||
Some((width, height)) => {
|
|
||||||
(*mmi).min_track = winapi::POINT { x: width as i32, y: height as i32 };
|
|
||||||
},
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
match window_state.attributes.max_dimensions {
|
|
||||||
Some((width, height)) => {
|
|
||||||
(*mmi).max_track = winapi::POINT { x: width as i32, y: height as i32 };
|
|
||||||
},
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => { }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
x if x == *super::WAKEUP_MSG_ID => {
|
|
||||||
// TODO: `Awakened` has been moved from the `WindowEvent` enum to the `Event` enum.
|
|
||||||
// This code needs to be updated to reflect this change.
|
|
||||||
//send_event(window, ::Event::Awakened);
|
|
||||||
0
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
|
||||||
const DEVICE_ID: ::DeviceId = ::DeviceId(super::DeviceId);
|
|
715
src/platform/windows/events_loop.rs
Normal file
715
src/platform/windows/events_loop.rs
Normal file
|
@ -0,0 +1,715 @@
|
||||||
|
//! An events loop on Win32 is a background thread.
|
||||||
|
//!
|
||||||
|
//! Creating an events loop spawns a thread and blocks it in a permanent Win32 events loop.
|
||||||
|
//! Destroying the events loop stops the thread.
|
||||||
|
//!
|
||||||
|
//! You can use the `execute_in_thread` method to execute some code in the background thread.
|
||||||
|
//! Since Win32 requires you to create a window in the right thread, you must use this method
|
||||||
|
//! to create a window.
|
||||||
|
//!
|
||||||
|
//! If you create a window whose class is set to `callback`, the window's events will be
|
||||||
|
//! propagated with `run_forever` and `poll_events`.
|
||||||
|
//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to
|
||||||
|
//! add a `WindowState` entry to a list of window to be used by the callback.
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::windows::ffi::OsStringExt;
|
||||||
|
use std::os::windows::io::AsRawHandle;
|
||||||
|
use std::ptr;
|
||||||
|
use std::sync::mpsc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use kernel32;
|
||||||
|
use shell32;
|
||||||
|
use user32;
|
||||||
|
use winapi;
|
||||||
|
|
||||||
|
use platform::platform::event;
|
||||||
|
use platform::platform::Cursor;
|
||||||
|
use platform::platform::WindowId;
|
||||||
|
use platform::platform::DEVICE_ID;
|
||||||
|
|
||||||
|
use ControlFlow;
|
||||||
|
use CursorState;
|
||||||
|
use Event;
|
||||||
|
use EventsLoopClosed;
|
||||||
|
use KeyboardInput;
|
||||||
|
use WindowAttributes;
|
||||||
|
use WindowEvent;
|
||||||
|
use WindowId as SuperWindowId;
|
||||||
|
use AxisId;
|
||||||
|
|
||||||
|
/// Contains information about states and the window that the callback is going to use.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WindowState {
|
||||||
|
/// Cursor to set at the next `WM_SETCURSOR` event received.
|
||||||
|
pub cursor: Cursor,
|
||||||
|
/// Cursor state to set at the next `WM_SETCURSOR` event received.
|
||||||
|
pub cursor_state: CursorState,
|
||||||
|
/// Used by `WM_GETMINMAXINFO`.
|
||||||
|
pub attributes: WindowAttributes,
|
||||||
|
/// Will contain `true` if the mouse is hovering the window.
|
||||||
|
pub mouse_in_window: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dummy object that allows inserting a window's state.
|
||||||
|
// We store a pointer in order to !impl Send and Sync.
|
||||||
|
pub struct Inserter(*mut u8);
|
||||||
|
|
||||||
|
impl Inserter {
|
||||||
|
/// Inserts a window's state for the callback to use. The state is removed automatically if the
|
||||||
|
/// callback receives a `WM_CLOSE` message for the window.
|
||||||
|
pub fn insert(&self, window: winapi::HWND, state: Arc<Mutex<WindowState>>) {
|
||||||
|
CONTEXT_STASH.with(|context_stash| {
|
||||||
|
let mut context_stash = context_stash.borrow_mut();
|
||||||
|
let was_in = context_stash.as_mut().unwrap().windows.insert(window, state);
|
||||||
|
assert!(was_in.is_none());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventsLoop {
|
||||||
|
// Id of the background thread from the Win32 API.
|
||||||
|
thread_id: winapi::DWORD,
|
||||||
|
// Receiver for the events. The sender is in the background thread.
|
||||||
|
receiver: mpsc::Receiver<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventsLoop {
|
||||||
|
pub fn new() -> EventsLoop {
|
||||||
|
// The main events transfer channel.
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
// Local channel in order to block the `new()` function until the background thread has
|
||||||
|
// an events queue.
|
||||||
|
let (local_block_tx, local_block_rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let thread = thread::spawn(move || {
|
||||||
|
CONTEXT_STASH.with(|context_stash| {
|
||||||
|
*context_stash.borrow_mut() = Some(ThreadLocalData {
|
||||||
|
sender: tx,
|
||||||
|
windows: HashMap::with_capacity(4),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut msg = mem::uninitialized();
|
||||||
|
|
||||||
|
// Calling `PostThreadMessageA` on a thread that does not have an events queue yet
|
||||||
|
// will fail. In order to avoid this situation, we call `PeekMessage` to initialize
|
||||||
|
// it.
|
||||||
|
user32::PeekMessageA(&mut msg, ptr::null_mut(), 0, 0, 0);
|
||||||
|
// Then only we unblock the `new()` function. We are sure that we don't call
|
||||||
|
// `PostThreadMessageA()` before `new()` returns.
|
||||||
|
local_block_tx.send(()).unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if user32::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
|
||||||
|
// Only happens if the message is `WM_QUIT`.
|
||||||
|
debug_assert_eq!(msg.message, winapi::WM_QUIT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match msg.message {
|
||||||
|
x if x == *EXEC_MSG_ID => {
|
||||||
|
let mut function: Box<Box<FnMut(Inserter)>> = Box::from_raw(msg.wParam as usize as *mut _);
|
||||||
|
function(Inserter(ptr::null_mut()));
|
||||||
|
},
|
||||||
|
x if x == *WAKEUP_MSG_ID => {
|
||||||
|
send_event(Event::Awakened);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
// Calls `callback` below.
|
||||||
|
user32::TranslateMessage(&msg);
|
||||||
|
user32::DispatchMessageW(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Blocks this function until the background thread has an events loop. See other comments.
|
||||||
|
local_block_rx.recv().unwrap();
|
||||||
|
|
||||||
|
EventsLoop {
|
||||||
|
thread_id: unsafe { kernel32::GetThreadId(thread.as_raw_handle()) },
|
||||||
|
receiver: rx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||||
|
where F: FnMut(Event)
|
||||||
|
{
|
||||||
|
loop {
|
||||||
|
let event = match self.receiver.try_recv() {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(_) => return
|
||||||
|
};
|
||||||
|
|
||||||
|
callback(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||||
|
where F: FnMut(Event) -> ControlFlow
|
||||||
|
{
|
||||||
|
loop {
|
||||||
|
let event = match self.receiver.recv() {
|
||||||
|
Ok(e) => e,
|
||||||
|
Err(_) => return
|
||||||
|
};
|
||||||
|
|
||||||
|
let flow = callback(event);
|
||||||
|
match flow {
|
||||||
|
ControlFlow::Continue => continue,
|
||||||
|
ControlFlow::Break => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||||
|
EventsLoopProxy {
|
||||||
|
thread_id: self.thread_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes a function in the background thread.
|
||||||
|
///
|
||||||
|
/// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent
|
||||||
|
/// to the unstable FnBox.
|
||||||
|
///
|
||||||
|
/// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is
|
||||||
|
/// removed automatically if the callback receives a `WM_CLOSE` message for the window.
|
||||||
|
pub(super) fn execute_in_thread<F>(&self, function: F)
|
||||||
|
where F: FnMut(Inserter) + Send + 'static
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let boxed = Box::new(function) as Box<FnMut(_)>;
|
||||||
|
let boxed2 = Box::new(boxed);
|
||||||
|
|
||||||
|
let raw = Box::into_raw(boxed2);
|
||||||
|
|
||||||
|
let res = user32::PostThreadMessageA(self.thread_id, *EXEC_MSG_ID,
|
||||||
|
raw as *mut () as usize as winapi::WPARAM, 0);
|
||||||
|
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen
|
||||||
|
// as the events loop is still alive) or if the queue is full.
|
||||||
|
assert!(res != 0, "PostThreadMessage failed ; is the messages queue full?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EventsLoop {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// Posting `WM_QUIT` will cause `GetMessage` to stop.
|
||||||
|
user32::PostThreadMessageA(self.thread_id, winapi::WM_QUIT, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventsLoopProxy {
|
||||||
|
thread_id: winapi::DWORD,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventsLoopProxy {
|
||||||
|
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||||
|
unsafe {
|
||||||
|
if user32::PostThreadMessageA(self.thread_id, *WAKEUP_MSG_ID, 0, 0) != 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// https://msdn.microsoft.com/fr-fr/library/windows/desktop/ms644946(v=vs.85).aspx
|
||||||
|
// > If the function fails, the return value is zero. To get extended error
|
||||||
|
// > information, call GetLastError. GetLastError returns ERROR_INVALID_THREAD_ID
|
||||||
|
// > if idThread is not a valid thread identifier, or if the thread specified by
|
||||||
|
// > idThread does not have a message queue. GetLastError returns
|
||||||
|
// > ERROR_NOT_ENOUGH_QUOTA when the message limit is hit.
|
||||||
|
// TODO: handle ERROR_NOT_ENOUGH_QUOTA
|
||||||
|
Err(EventsLoopClosed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
// Message sent by the `EventsLoopProxy` when we want to wake up the thread.
|
||||||
|
// WPARAM and LPARAM are unused.
|
||||||
|
static ref WAKEUP_MSG_ID: u32 = {
|
||||||
|
unsafe {
|
||||||
|
user32::RegisterWindowMessageA("Winit::WakeupMsg".as_ptr() as *const i8)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Message sent when we want to execute a closure in the thread.
|
||||||
|
// WPARAM contains a Box<Box<FnMut()>> that must be retreived with `Box::from_raw`,
|
||||||
|
// and LPARAM is unused.
|
||||||
|
static ref EXEC_MSG_ID: u32 = {
|
||||||
|
unsafe {
|
||||||
|
user32::RegisterWindowMessageA("Winit::ExecMsg".as_ptr() as *const i8)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's no parameters passed to the callback function, so it needs to get its context stashed
|
||||||
|
// in a thread-local variable.
|
||||||
|
thread_local!(static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None));
|
||||||
|
struct ThreadLocalData {
|
||||||
|
sender: mpsc::Sender<Event>,
|
||||||
|
windows: HashMap<winapi::HWND, Arc<Mutex<WindowState>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility function that dispatches an event on the current thread.
|
||||||
|
fn send_event(event: Event) {
|
||||||
|
CONTEXT_STASH.with(|context_stash| {
|
||||||
|
let context_stash = context_stash.borrow();
|
||||||
|
let _ = context_stash.as_ref().unwrap().sender.send(event); // Ignoring if closed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Any window whose callback is configured to this function will have its events propagated
|
||||||
|
/// through the events loop of the thread the window was created in.
|
||||||
|
//
|
||||||
|
// This is the callback that is called by `DispatchMessage` in the events loop.
|
||||||
|
//
|
||||||
|
// Returning 0 tells the Win32 API that the message has been processed.
|
||||||
|
// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary
|
||||||
|
pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
||||||
|
wparam: winapi::WPARAM, lparam: winapi::LPARAM)
|
||||||
|
-> winapi::LRESULT
|
||||||
|
{
|
||||||
|
match msg {
|
||||||
|
winapi::WM_DESTROY => {
|
||||||
|
use events::WindowEvent::Closed;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: Closed
|
||||||
|
});
|
||||||
|
CONTEXT_STASH.with(|context_stash| {
|
||||||
|
let mut context_stash = context_stash.borrow_mut();
|
||||||
|
context_stash.as_mut().unwrap().windows.remove(&window);
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_ERASEBKGND => {
|
||||||
|
1
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_SIZE => {
|
||||||
|
use events::WindowEvent::Resized;
|
||||||
|
let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
|
||||||
|
let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: Resized(w, h),
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_MOVE => {
|
||||||
|
use events::WindowEvent::Moved;
|
||||||
|
let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
|
||||||
|
let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: Moved(x, y),
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_CHAR => {
|
||||||
|
use std::mem;
|
||||||
|
use events::WindowEvent::ReceivedCharacter;
|
||||||
|
let chr: char = mem::transmute(wparam as u32);
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: ReceivedCharacter(chr),
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
// Prevents default windows menu hotkeys playing unwanted
|
||||||
|
// "ding" sounds. Alternatively could check for WM_SYSCOMMAND
|
||||||
|
// with wparam being SC_KEYMENU, but this may prevent some
|
||||||
|
// other unwanted default hotkeys as well.
|
||||||
|
winapi::WM_SYSCHAR => {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
winapi::WM_MOUSEMOVE => {
|
||||||
|
use events::WindowEvent::{MouseEntered, MouseMoved};
|
||||||
|
let mouse_outside_window = CONTEXT_STASH.with(|context_stash| {
|
||||||
|
let mut context_stash = context_stash.borrow_mut();
|
||||||
|
if let Some(context_stash) = context_stash.as_mut() {
|
||||||
|
if let Some(w) = context_stash.windows.get_mut(&window) {
|
||||||
|
let mut w = w.lock().unwrap();
|
||||||
|
if !w.mouse_in_window {
|
||||||
|
w.mouse_in_window = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
});
|
||||||
|
|
||||||
|
if mouse_outside_window {
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseEntered { device_id: DEVICE_ID },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calling TrackMouseEvent in order to receive mouse leave events.
|
||||||
|
user32::TrackMouseEvent(&mut winapi::TRACKMOUSEEVENT {
|
||||||
|
cbSize: mem::size_of::<winapi::TRACKMOUSEEVENT>() as winapi::DWORD,
|
||||||
|
dwFlags: winapi::TME_LEAVE,
|
||||||
|
hwndTrack: window,
|
||||||
|
dwHoverTime: winapi::HOVER_DEFAULT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = winapi::GET_X_LPARAM(lparam) as f64;
|
||||||
|
let y = winapi::GET_Y_LPARAM(lparam) as f64;
|
||||||
|
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseMoved { device_id: DEVICE_ID, position: (x, y) },
|
||||||
|
});
|
||||||
|
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_MOUSELEAVE => {
|
||||||
|
use events::WindowEvent::MouseLeft;
|
||||||
|
let mouse_in_window = CONTEXT_STASH.with(|context_stash| {
|
||||||
|
let mut context_stash = context_stash.borrow_mut();
|
||||||
|
if let Some(context_stash) = context_stash.as_mut() {
|
||||||
|
if let Some(w) = context_stash.windows.get_mut(&window) {
|
||||||
|
let mut w = w.lock().unwrap();
|
||||||
|
if w.mouse_in_window {
|
||||||
|
w.mouse_in_window = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
});
|
||||||
|
|
||||||
|
if mouse_in_window {
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseLeft { device_id: DEVICE_ID }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_MOUSEWHEEL => {
|
||||||
|
use events::WindowEvent::MouseWheel;
|
||||||
|
use events::MouseScrollDelta::LineDelta;
|
||||||
|
use events::TouchPhase;
|
||||||
|
|
||||||
|
let value = (wparam >> 16) as i16;
|
||||||
|
let value = value as i32;
|
||||||
|
let value = value as f32 / winapi::WHEEL_DELTA as f32;
|
||||||
|
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved },
|
||||||
|
});
|
||||||
|
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_KEYDOWN | winapi::WM_SYSKEYDOWN => {
|
||||||
|
use events::ElementState::Pressed;
|
||||||
|
if msg == winapi::WM_SYSKEYDOWN && wparam as i32 == winapi::VK_F4 {
|
||||||
|
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||||
|
} else {
|
||||||
|
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: WindowEvent::KeyboardInput {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
input: KeyboardInput {
|
||||||
|
state: Pressed,
|
||||||
|
scancode: scancode,
|
||||||
|
virtual_keycode: vkey,
|
||||||
|
modifiers: event::get_key_mods(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_KEYUP | winapi::WM_SYSKEYUP => {
|
||||||
|
use events::ElementState::Released;
|
||||||
|
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: WindowEvent::KeyboardInput {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
input: KeyboardInput {
|
||||||
|
state: Released,
|
||||||
|
scancode: scancode,
|
||||||
|
virtual_keycode: vkey,
|
||||||
|
modifiers: event::get_key_mods(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_LBUTTONDOWN => {
|
||||||
|
use events::WindowEvent::MouseInput;
|
||||||
|
use events::MouseButton::Left;
|
||||||
|
use events::ElementState::Pressed;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left }
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_LBUTTONUP => {
|
||||||
|
use events::WindowEvent::MouseInput;
|
||||||
|
use events::MouseButton::Left;
|
||||||
|
use events::ElementState::Released;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left }
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_RBUTTONDOWN => {
|
||||||
|
use events::WindowEvent::MouseInput;
|
||||||
|
use events::MouseButton::Right;
|
||||||
|
use events::ElementState::Pressed;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right }
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_RBUTTONUP => {
|
||||||
|
use events::WindowEvent::MouseInput;
|
||||||
|
use events::MouseButton::Right;
|
||||||
|
use events::ElementState::Released;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right }
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_MBUTTONDOWN => {
|
||||||
|
use events::WindowEvent::MouseInput;
|
||||||
|
use events::MouseButton::Middle;
|
||||||
|
use events::ElementState::Pressed;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle }
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_MBUTTONUP => {
|
||||||
|
use events::WindowEvent::MouseInput;
|
||||||
|
use events::MouseButton::Middle;
|
||||||
|
use events::ElementState::Released;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle }
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_XBUTTONDOWN => {
|
||||||
|
use events::WindowEvent::MouseInput;
|
||||||
|
use events::MouseButton::Other;
|
||||||
|
use events::ElementState::Pressed;
|
||||||
|
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; // waiting on PR for winapi to add GET_XBUTTON_WPARAM
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8) }
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_XBUTTONUP => {
|
||||||
|
use events::WindowEvent::MouseInput;
|
||||||
|
use events::MouseButton::Other;
|
||||||
|
use events::ElementState::Released;
|
||||||
|
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8) }
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_INPUT => {
|
||||||
|
use events::DeviceEvent::Motion;
|
||||||
|
let mut data: winapi::RAWINPUT = mem::uninitialized();
|
||||||
|
let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT;
|
||||||
|
user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT,
|
||||||
|
mem::transmute(&mut data), &mut data_size,
|
||||||
|
mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT);
|
||||||
|
|
||||||
|
if data.header.dwType == winapi::RIM_TYPEMOUSE {
|
||||||
|
if data.mouse.usFlags & winapi::MOUSE_MOVE_RELATIVE == winapi::MOUSE_MOVE_RELATIVE {
|
||||||
|
let x = data.mouse.lLastX as f64;
|
||||||
|
let y = data.mouse.lLastY as f64;
|
||||||
|
|
||||||
|
if x != 0.0 {
|
||||||
|
send_event(Event::DeviceEvent {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
event: Motion { axis: AxisId(0), value: x }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if y != 0.0 {
|
||||||
|
send_event(Event::DeviceEvent {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
event: Motion { axis: AxisId(1), value: y }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_SETFOCUS => {
|
||||||
|
use events::WindowEvent::Focused;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: Focused(true)
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_KILLFOCUS => {
|
||||||
|
use events::WindowEvent::Focused;
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: Focused(false)
|
||||||
|
});
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_SETCURSOR => {
|
||||||
|
let call_def_window_proc = CONTEXT_STASH.with(|context_stash| {
|
||||||
|
let cstash = context_stash.borrow();
|
||||||
|
let mut call_def_window_proc = false;
|
||||||
|
if let Some(cstash) = cstash.as_ref() {
|
||||||
|
if let Some(w_stash) = cstash.windows.get(&window) {
|
||||||
|
if let Ok(window_state) = w_stash.lock() {
|
||||||
|
if window_state.mouse_in_window {
|
||||||
|
match window_state.cursor_state {
|
||||||
|
CursorState::Normal => {
|
||||||
|
user32::SetCursor(user32::LoadCursorW(
|
||||||
|
ptr::null_mut(),
|
||||||
|
window_state.cursor));
|
||||||
|
},
|
||||||
|
CursorState::Grab | CursorState::Hide => {
|
||||||
|
user32::SetCursor(ptr::null_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
call_def_window_proc = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
call_def_window_proc
|
||||||
|
});
|
||||||
|
|
||||||
|
if call_def_window_proc {
|
||||||
|
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_DROPFILES => {
|
||||||
|
use events::WindowEvent::DroppedFile;
|
||||||
|
|
||||||
|
let hdrop = wparam as winapi::HDROP;
|
||||||
|
let mut pathbuf: [u16; winapi::MAX_PATH] = mem::uninitialized();
|
||||||
|
let num_drops = shell32::DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
|
||||||
|
|
||||||
|
for i in 0..num_drops {
|
||||||
|
let nch = shell32::DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(),
|
||||||
|
winapi::MAX_PATH as u32) as usize;
|
||||||
|
if nch > 0 {
|
||||||
|
send_event(Event::WindowEvent {
|
||||||
|
window_id: SuperWindowId(WindowId(window)),
|
||||||
|
event: DroppedFile(OsString::from_wide(&pathbuf[0..nch]).into())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shell32::DragFinish(hdrop);
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
winapi::WM_GETMINMAXINFO => {
|
||||||
|
// Equivalent to the windows api [MINMAXINFO](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632605%28v=vs.85%29.aspx)
|
||||||
|
// struct. Used because winapi-rs doesn't have this declared.
|
||||||
|
// TODO: replace with struct from winapi-rs
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct MinMaxInfo {
|
||||||
|
reserved: winapi::POINT, // Do not use/change
|
||||||
|
max_size: winapi::POINT,
|
||||||
|
max_position: winapi::POINT,
|
||||||
|
min_track: winapi::POINT,
|
||||||
|
max_track: winapi::POINT
|
||||||
|
}
|
||||||
|
|
||||||
|
let mmi = lparam as *mut MinMaxInfo;
|
||||||
|
//(*mmi).max_position = winapi::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor.
|
||||||
|
//(*mmi).max_size = winapi::POINT { x: .., y: .. }; // The dimensions of the primary monitor.
|
||||||
|
|
||||||
|
CONTEXT_STASH.with(|context_stash| {
|
||||||
|
if let Some(cstash) = context_stash.borrow().as_ref() {
|
||||||
|
if let Some(wstash) = cstash.windows.get(&window) {
|
||||||
|
let window_state = wstash.lock().unwrap();
|
||||||
|
|
||||||
|
match window_state.attributes.min_dimensions {
|
||||||
|
Some((width, height)) => {
|
||||||
|
(*mmi).min_track = winapi::POINT { x: width as i32, y: height as i32 };
|
||||||
|
},
|
||||||
|
None => { }
|
||||||
|
}
|
||||||
|
|
||||||
|
match window_state.attributes.max_dimensions {
|
||||||
|
Some((width, height)) => {
|
||||||
|
(*mmi).max_track = winapi::POINT { x: width as i32, y: height as i32 };
|
||||||
|
},
|
||||||
|
None => { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
0
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,259 +0,0 @@
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::io;
|
|
||||||
use std::ptr;
|
|
||||||
use std::mem;
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
use super::callback;
|
|
||||||
use super::WindowState;
|
|
||||||
use super::Window;
|
|
||||||
use super::MonitorId;
|
|
||||||
use super::WindowWrapper;
|
|
||||||
use super::PlatformSpecificWindowBuilderAttributes;
|
|
||||||
|
|
||||||
use CreationError;
|
|
||||||
use CreationError::OsError;
|
|
||||||
use CursorState;
|
|
||||||
use WindowAttributes;
|
|
||||||
|
|
||||||
use std::ffi::{OsStr};
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
|
||||||
use std::sync::mpsc::channel;
|
|
||||||
|
|
||||||
use winapi;
|
|
||||||
use kernel32;
|
|
||||||
use dwmapi;
|
|
||||||
use user32;
|
|
||||||
|
|
||||||
pub fn new_window(window: &WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError> {
|
|
||||||
let window = window.clone();
|
|
||||||
let attribs = pl_attribs.clone();
|
|
||||||
// initializing variables to be sent to the task
|
|
||||||
|
|
||||||
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
|
|
||||||
// `GetMessage` must be called in the same thread as CreateWindow, so we create a new thread
|
|
||||||
// dedicated to this window.
|
|
||||||
thread::spawn(move || {
|
|
||||||
unsafe {
|
|
||||||
// creating and sending the `Window`
|
|
||||||
match init(title, &window, attribs) {
|
|
||||||
Ok(w) => tx.send(Ok(w)).ok(),
|
|
||||||
Err(e) => {
|
|
||||||
tx.send(Err(e)).ok();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// now that the `Window` struct is initialized, the main `Window::new()` function will
|
|
||||||
// return and this events loop will run in parallel
|
|
||||||
loop {
|
|
||||||
let mut msg = mem::uninitialized();
|
|
||||||
|
|
||||||
if user32::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
user32::TranslateMessage(&msg);
|
|
||||||
user32::DispatchMessageW(&msg); // calls `callback` (see the callback module)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rx.recv().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn init(title: Vec<u16>, window: &WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError> {
|
|
||||||
// registering the window class
|
|
||||||
let class_name = register_window_class();
|
|
||||||
|
|
||||||
// building a RECT object with coordinates
|
|
||||||
let mut rect = winapi::RECT {
|
|
||||||
left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as winapi::LONG,
|
|
||||||
top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as winapi::LONG,
|
|
||||||
};
|
|
||||||
|
|
||||||
// switching to fullscreen if necessary
|
|
||||||
// this means adjusting the window's position so that it overlaps the right monitor,
|
|
||||||
// and change the monitor's resolution if necessary
|
|
||||||
if window.monitor.is_some() {
|
|
||||||
let monitor = window.monitor.as_ref().unwrap();
|
|
||||||
try!(switch_to_fullscreen(&mut rect, monitor));
|
|
||||||
}
|
|
||||||
|
|
||||||
// computing the style and extended style of the window
|
|
||||||
let (ex_style, style) = if window.monitor.is_some() || !window.decorations {
|
|
||||||
(winapi::WS_EX_APPWINDOW,
|
|
||||||
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
|
||||||
if pl_attribs.parent.is_some() {
|
|
||||||
winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(winapi::WS_EX_APPWINDOW | winapi::WS_EX_WINDOWEDGE,
|
|
||||||
winapi::WS_OVERLAPPEDWINDOW | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN)
|
|
||||||
};
|
|
||||||
|
|
||||||
// adjusting the window coordinates using the style
|
|
||||||
user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
|
||||||
|
|
||||||
// creating the real window this time, by using the functions in `extra_functions`
|
|
||||||
let real_window = {
|
|
||||||
let (width, height) = if window.monitor.is_some() || window.dimensions.is_some() {
|
|
||||||
(Some(rect.right - rect.left), Some(rect.bottom - rect.top))
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let (x, y) = if window.monitor.is_some() {
|
|
||||||
(Some(rect.left), Some(rect.top))
|
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut style = if !window.visible {
|
|
||||||
style
|
|
||||||
} else {
|
|
||||||
style | winapi::WS_VISIBLE
|
|
||||||
};
|
|
||||||
|
|
||||||
if pl_attribs.parent.is_some() {
|
|
||||||
style |= winapi::WS_CHILD;
|
|
||||||
}
|
|
||||||
|
|
||||||
let handle = user32::CreateWindowExW(ex_style | winapi::WS_EX_ACCEPTFILES,
|
|
||||||
class_name.as_ptr(),
|
|
||||||
title.as_ptr() as winapi::LPCWSTR,
|
|
||||||
style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN,
|
|
||||||
x.unwrap_or(winapi::CW_USEDEFAULT), y.unwrap_or(winapi::CW_USEDEFAULT),
|
|
||||||
width.unwrap_or(winapi::CW_USEDEFAULT), height.unwrap_or(winapi::CW_USEDEFAULT),
|
|
||||||
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
|
||||||
ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()),
|
|
||||||
ptr::null_mut());
|
|
||||||
|
|
||||||
if handle.is_null() {
|
|
||||||
return Err(OsError(format!("CreateWindowEx function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
let hdc = user32::GetDC(handle);
|
|
||||||
if hdc.is_null() {
|
|
||||||
return Err(OsError(format!("GetDC function failed: {}",
|
|
||||||
format!("{}", io::Error::last_os_error()))));
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowWrapper(handle, hdc)
|
|
||||||
};
|
|
||||||
|
|
||||||
// making the window transparent
|
|
||||||
if window.transparent {
|
|
||||||
let bb = winapi::DWM_BLURBEHIND {
|
|
||||||
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
|
||||||
fEnable: 1,
|
|
||||||
hRgnBlur: ptr::null_mut(),
|
|
||||||
fTransitionOnMaximized: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// calling SetForegroundWindow if fullscreen
|
|
||||||
if window.monitor.is_some() {
|
|
||||||
user32::SetForegroundWindow(real_window.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creating a mutex to track the current window state
|
|
||||||
let window_state = Arc::new(Mutex::new(WindowState {
|
|
||||||
cursor: winapi::IDC_ARROW, // use arrow by default
|
|
||||||
cursor_state: CursorState::Normal,
|
|
||||||
attributes: window.clone()
|
|
||||||
}));
|
|
||||||
|
|
||||||
// filling the CONTEXT_STASH task-local storage so that we can start receiving events
|
|
||||||
let events_receiver = {
|
|
||||||
let (tx, rx) = channel();
|
|
||||||
let mut tx = Some(tx);
|
|
||||||
callback::CONTEXT_STASH.with(|context_stash| {
|
|
||||||
let data = callback::ThreadLocalData {
|
|
||||||
win: real_window.0,
|
|
||||||
sender: tx.take().unwrap(),
|
|
||||||
window_state: window_state.clone(),
|
|
||||||
mouse_in_window: false
|
|
||||||
};
|
|
||||||
(*context_stash.borrow_mut()) = Some(data);
|
|
||||||
});
|
|
||||||
rx
|
|
||||||
};
|
|
||||||
|
|
||||||
// building the struct
|
|
||||||
Ok(Window {
|
|
||||||
window: real_window,
|
|
||||||
events_receiver: events_receiver,
|
|
||||||
window_state: window_state,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn register_window_class() -> Vec<u16> {
|
|
||||||
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let class = winapi::WNDCLASSEXW {
|
|
||||||
cbSize: mem::size_of::<winapi::WNDCLASSEXW>() as winapi::UINT,
|
|
||||||
style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC,
|
|
||||||
lpfnWndProc: Some(callback::callback),
|
|
||||||
cbClsExtra: 0,
|
|
||||||
cbWndExtra: 0,
|
|
||||||
hInstance: kernel32::GetModuleHandleW(ptr::null()),
|
|
||||||
hIcon: ptr::null_mut(),
|
|
||||||
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
|
||||||
hbrBackground: ptr::null_mut(),
|
|
||||||
lpszMenuName: ptr::null(),
|
|
||||||
lpszClassName: class_name.as_ptr(),
|
|
||||||
hIconSm: ptr::null_mut(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// We ignore errors because registering the same window class twice would trigger
|
|
||||||
// an error, and because errors here are detected during CreateWindowEx anyway.
|
|
||||||
// Also since there is no weird element in the struct, there is no reason for this
|
|
||||||
// call to fail.
|
|
||||||
user32::RegisterClassExW(&class);
|
|
||||||
|
|
||||||
class_name
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn switch_to_fullscreen(rect: &mut winapi::RECT, monitor: &MonitorId)
|
|
||||||
-> Result<(), CreationError>
|
|
||||||
{
|
|
||||||
// adjusting the rect
|
|
||||||
{
|
|
||||||
let pos = monitor.get_position();
|
|
||||||
rect.left += pos.0 as winapi::LONG;
|
|
||||||
rect.right += pos.0 as winapi::LONG;
|
|
||||||
rect.top += pos.1 as winapi::LONG;
|
|
||||||
rect.bottom += pos.1 as winapi::LONG;
|
|
||||||
}
|
|
||||||
|
|
||||||
// changing device settings
|
|
||||||
let mut screen_settings: winapi::DEVMODEW = mem::zeroed();
|
|
||||||
screen_settings.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD;
|
|
||||||
screen_settings.dmPelsWidth = (rect.right - rect.left) as winapi::DWORD;
|
|
||||||
screen_settings.dmPelsHeight = (rect.bottom - rect.top) as winapi::DWORD;
|
|
||||||
screen_settings.dmBitsPerPel = 32; // TODO: ?
|
|
||||||
screen_settings.dmFields = winapi::DM_BITSPERPEL | winapi::DM_PELSWIDTH | winapi::DM_PELSHEIGHT;
|
|
||||||
|
|
||||||
let result = user32::ChangeDisplaySettingsExW(monitor.get_adapter_name().as_ptr(),
|
|
||||||
&mut screen_settings, ptr::null_mut(),
|
|
||||||
winapi::CDS_FULLSCREEN, ptr::null_mut());
|
|
||||||
|
|
||||||
if result != winapi::DISP_CHANGE_SUCCESSFUL {
|
|
||||||
return Err(OsError(format!("ChangeDisplaySettings failed: {}", result)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,21 +1,10 @@
|
||||||
#![cfg(target_os = "windows")]
|
#![cfg(target_os = "windows")]
|
||||||
|
|
||||||
use std::mem;
|
use winapi;
|
||||||
use std::ptr;
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::os::windows::ffi::OsStrExt;
|
|
||||||
use std::os::raw::c_int;
|
|
||||||
use std::sync::{
|
|
||||||
Arc,
|
|
||||||
Mutex
|
|
||||||
};
|
|
||||||
use std::sync::mpsc::Receiver;
|
|
||||||
use {CreationError, WindowEvent as Event, MouseCursor};
|
|
||||||
use CursorState;
|
|
||||||
|
|
||||||
use WindowAttributes;
|
pub use self::events_loop::{EventsLoop, EventsLoopProxy};
|
||||||
|
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||||
gen_api_transition!();
|
pub use self::window::Window;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
|
@ -25,361 +14,23 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||||
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
|
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
|
||||||
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
|
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
// TODO: document what this means
|
||||||
pub struct PlatformSpecificHeadlessBuilderAttributes;
|
|
||||||
|
|
||||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
|
||||||
|
|
||||||
use winapi;
|
|
||||||
use user32;
|
|
||||||
use kernel32;
|
|
||||||
|
|
||||||
mod callback;
|
|
||||||
mod event;
|
|
||||||
mod init;
|
|
||||||
mod monitor;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref WAKEUP_MSG_ID: u32 = unsafe { user32::RegisterWindowMessageA("Glutin::EventID".as_ptr() as *const i8) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cursor
|
|
||||||
pub type Cursor = *const winapi::wchar_t;
|
pub type Cursor = *const winapi::wchar_t;
|
||||||
|
|
||||||
/// Contains information about states and the window for the callback.
|
// TODO: remove
|
||||||
#[derive(Clone)]
|
pub type Window2 = Window;
|
||||||
pub struct WindowState {
|
|
||||||
pub cursor: Cursor,
|
|
||||||
pub cursor_state: CursorState,
|
|
||||||
pub attributes: WindowAttributes
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The Win32 implementation of the main `Window` object.
|
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||||
pub struct Window {
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
/// Main handle for the window.
|
pub struct DeviceId;
|
||||||
window: WindowWrapper,
|
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||||
|
|
||||||
/// Receiver for the events dispatched by the window callback.
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
events_receiver: Receiver<Event>,
|
pub struct WindowId(winapi::HWND);
|
||||||
|
unsafe impl Send for WindowId {}
|
||||||
|
unsafe impl Sync for WindowId {}
|
||||||
|
|
||||||
/// The current window state.
|
mod event;
|
||||||
window_state: Arc<Mutex<WindowState>>,
|
mod events_loop;
|
||||||
}
|
mod monitor;
|
||||||
|
mod window;
|
||||||
unsafe impl Send for Window {}
|
|
||||||
unsafe impl Sync for Window {}
|
|
||||||
|
|
||||||
/// A simple wrapper that destroys the window when it is destroyed.
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub struct WindowWrapper(winapi::HWND, winapi::HDC);
|
|
||||||
|
|
||||||
impl Drop for WindowWrapper {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
user32::DestroyWindow(self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct WindowProxy {
|
|
||||||
hwnd: winapi::HWND,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for WindowProxy {}
|
|
||||||
unsafe impl Sync for WindowProxy {}
|
|
||||||
|
|
||||||
impl WindowProxy {
|
|
||||||
#[inline]
|
|
||||||
pub fn wakeup_event_loop(&self) {
|
|
||||||
unsafe {
|
|
||||||
user32::PostMessageA(self.hwnd, *WAKEUP_MSG_ID, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Window {
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
pub fn new(window: &WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
|
||||||
-> Result<Window, CreationError>
|
|
||||||
{
|
|
||||||
init::new_window(window, pl_attribs)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
///
|
|
||||||
/// Calls SetWindowText on the HWND.
|
|
||||||
pub fn set_title(&self, text: &str) {
|
|
||||||
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
user32::SetWindowTextW(self.window.0, text.as_ptr() as winapi::LPCWSTR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn show(&self) {
|
|
||||||
unsafe {
|
|
||||||
user32::ShowWindow(self.window.0, winapi::SW_SHOW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn hide(&self) {
|
|
||||||
unsafe {
|
|
||||||
user32::ShowWindow(self.window.0, winapi::SW_HIDE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
let mut placement: winapi::WINDOWPLACEMENT = unsafe { mem::zeroed() };
|
|
||||||
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT;
|
|
||||||
|
|
||||||
if unsafe { user32::GetWindowPlacement(self.window.0, &mut placement) } == 0 {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let ref rect = placement.rcNormalPosition;
|
|
||||||
Some((rect.left as i32, rect.top as i32))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
pub fn set_position(&self, x: i32, y: i32) {
|
|
||||||
unsafe {
|
|
||||||
user32::SetWindowPos(self.window.0, ptr::null_mut(), x as c_int, y as c_int,
|
|
||||||
0, 0, winapi::SWP_NOZORDER | winapi::SWP_NOSIZE);
|
|
||||||
user32::UpdateWindow(self.window.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
|
||||||
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
|
|
||||||
|
|
||||||
if unsafe { user32::GetClientRect(self.window.0, &mut rect) } == 0 {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((
|
|
||||||
(rect.right - rect.left) as u32,
|
|
||||||
(rect.bottom - rect.top) as u32
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
#[inline]
|
|
||||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
|
||||||
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
|
|
||||||
|
|
||||||
if unsafe { user32::GetWindowRect(self.window.0, &mut rect) } == 0 {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((
|
|
||||||
(rect.right - rect.left) as u32,
|
|
||||||
(rect.bottom - rect.top) as u32
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
|
||||||
unsafe {
|
|
||||||
// Calculate the outer size based upon the specified inner size
|
|
||||||
let mut rect = winapi::RECT { top: 0, left: 0, bottom: y as winapi::LONG, right: x as winapi::LONG };
|
|
||||||
let dw_style = user32::GetWindowLongA(self.window.0, winapi::GWL_STYLE) as winapi::DWORD;
|
|
||||||
let b_menu = !user32::GetMenu(self.window.0).is_null() as winapi::BOOL;
|
|
||||||
let dw_style_ex = user32::GetWindowLongA(self.window.0, winapi::GWL_EXSTYLE) as winapi::DWORD;
|
|
||||||
user32::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
|
||||||
let outer_x = (rect.right - rect.left).abs() as c_int;
|
|
||||||
let outer_y = (rect.top - rect.bottom).abs() as c_int;
|
|
||||||
|
|
||||||
user32::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
|
||||||
winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION | winapi::SWP_NOMOVE);
|
|
||||||
user32::UpdateWindow(self.window.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
|
||||||
WindowProxy { hwnd: self.window.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
#[inline]
|
|
||||||
pub fn poll_events(&self) -> PollEventsIterator {
|
|
||||||
PollEventsIterator {
|
|
||||||
window: self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See the docs in the crate root file.
|
|
||||||
#[inline]
|
|
||||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
|
||||||
WaitEventsIterator {
|
|
||||||
window: self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
|
||||||
// What should this return on win32?
|
|
||||||
// It could be GetDC(NULL), but that requires a ReleaseDC()
|
|
||||||
// to avoid leaking the DC.
|
|
||||||
ptr::null_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
|
||||||
self.window.0 as *mut ::libc::c_void
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
|
||||||
let cursor_id = match _cursor {
|
|
||||||
MouseCursor::Arrow | MouseCursor::Default => winapi::IDC_ARROW,
|
|
||||||
MouseCursor::Hand => winapi::IDC_HAND,
|
|
||||||
MouseCursor::Crosshair => winapi::IDC_CROSS,
|
|
||||||
MouseCursor::Text | MouseCursor::VerticalText => winapi::IDC_IBEAM,
|
|
||||||
MouseCursor::NotAllowed | MouseCursor::NoDrop => winapi::IDC_NO,
|
|
||||||
MouseCursor::EResize => winapi::IDC_SIZEWE,
|
|
||||||
MouseCursor::NResize => winapi::IDC_SIZENS,
|
|
||||||
MouseCursor::WResize => winapi::IDC_SIZEWE,
|
|
||||||
MouseCursor::SResize => winapi::IDC_SIZENS,
|
|
||||||
MouseCursor::EwResize | MouseCursor::ColResize => winapi::IDC_SIZEWE,
|
|
||||||
MouseCursor::NsResize | MouseCursor::RowResize => winapi::IDC_SIZENS,
|
|
||||||
MouseCursor::Wait | MouseCursor::Progress => winapi::IDC_WAIT,
|
|
||||||
MouseCursor::Help => winapi::IDC_HELP,
|
|
||||||
_ => winapi::IDC_ARROW, // use arrow for the missing cases.
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut cur = self.window_state.lock().unwrap();
|
|
||||||
cur.cursor = cursor_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
|
||||||
let mut current_state = self.window_state.lock().unwrap();
|
|
||||||
|
|
||||||
let foreground_thread_id = unsafe { user32::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) };
|
|
||||||
let current_thread_id = unsafe { kernel32::GetCurrentThreadId() };
|
|
||||||
|
|
||||||
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 1) };
|
|
||||||
|
|
||||||
let res = match (state, current_state.cursor_state) {
|
|
||||||
(CursorState::Normal, CursorState::Normal) => Ok(()),
|
|
||||||
(CursorState::Hide, CursorState::Hide) => Ok(()),
|
|
||||||
(CursorState::Grab, CursorState::Grab) => Ok(()),
|
|
||||||
|
|
||||||
(CursorState::Hide, CursorState::Normal) => {
|
|
||||||
current_state.cursor_state = CursorState::Hide;
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
|
|
||||||
(CursorState::Normal, CursorState::Hide) => {
|
|
||||||
current_state.cursor_state = CursorState::Normal;
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
|
|
||||||
(CursorState::Grab, CursorState::Normal) | (CursorState::Grab, CursorState::Hide) => {
|
|
||||||
unsafe {
|
|
||||||
let mut rect = mem::uninitialized();
|
|
||||||
if user32::GetClientRect(self.window.0, &mut rect) == 0 {
|
|
||||||
return Err(format!("GetWindowRect failed"));
|
|
||||||
}
|
|
||||||
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.left));
|
|
||||||
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.right));
|
|
||||||
if user32::ClipCursor(&rect) == 0 {
|
|
||||||
return Err(format!("ClipCursor failed"));
|
|
||||||
}
|
|
||||||
current_state.cursor_state = CursorState::Grab;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
(CursorState::Normal, CursorState::Grab) => {
|
|
||||||
unsafe {
|
|
||||||
if user32::ClipCursor(ptr::null()) == 0 {
|
|
||||||
return Err(format!("ClipCursor failed"));
|
|
||||||
}
|
|
||||||
current_state.cursor_state = CursorState::Normal;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 0) };
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn hidpi_factor(&self) -> f32 {
|
|
||||||
1.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
|
||||||
let mut point = winapi::POINT {
|
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
if user32::ClientToScreen(self.window.0, &mut point) == 0 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if user32::SetCursorPos(point.x, point.y) == 0 {
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Window {
|
|
||||||
#[inline]
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
user32::PostMessageW(self.window.0, winapi::WM_DESTROY, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PollEventsIterator<'a> {
|
|
||||||
window: &'a Window,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for PollEventsIterator<'a> {
|
|
||||||
type Item = Event;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Event> {
|
|
||||||
self.window.events_receiver.try_recv().ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WaitEventsIterator<'a> {
|
|
||||||
window: &'a Window,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
|
||||||
type Item = Event;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn next(&mut self) -> Option<Event> {
|
|
||||||
self.window.events_receiver.recv().ok()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
494
src/platform/windows/window.rs
Normal file
494
src/platform/windows/window.rs
Normal file
|
@ -0,0 +1,494 @@
|
||||||
|
#![cfg(target_os = "windows")]
|
||||||
|
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::io;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw;
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
use std::ptr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
|
||||||
|
use platform::platform::events_loop;
|
||||||
|
use platform::platform::EventsLoop;
|
||||||
|
use platform::platform::PlatformSpecificWindowBuilderAttributes;
|
||||||
|
use platform::platform::MonitorId;
|
||||||
|
use platform::platform::WindowId;
|
||||||
|
|
||||||
|
use CreationError;
|
||||||
|
use CursorState;
|
||||||
|
use MouseCursor;
|
||||||
|
use WindowAttributes;
|
||||||
|
|
||||||
|
use dwmapi;
|
||||||
|
use kernel32;
|
||||||
|
use user32;
|
||||||
|
use winapi;
|
||||||
|
|
||||||
|
/// The Win32 implementation of the main `Window` object.
|
||||||
|
pub struct Window {
|
||||||
|
/// Main handle for the window.
|
||||||
|
window: WindowWrapper,
|
||||||
|
|
||||||
|
/// The current window state.
|
||||||
|
window_state: Arc<Mutex<events_loop::WindowState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Window {}
|
||||||
|
unsafe impl Sync for Window {}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
pub fn new(events_loop: &EventsLoop, w_attr: &WindowAttributes,
|
||||||
|
pl_attr: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
|
||||||
|
{
|
||||||
|
let mut w_attr = Some(w_attr.clone());
|
||||||
|
let mut pl_attr = Some(pl_attr.clone());
|
||||||
|
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
|
events_loop.execute_in_thread(move |inserter| {
|
||||||
|
// We dispatch an `init` function because of code style.
|
||||||
|
let win = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap(), inserter) };
|
||||||
|
let _ = tx.send(win);
|
||||||
|
});
|
||||||
|
|
||||||
|
rx.recv().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_title(&self, text: &str) {
|
||||||
|
unsafe {
|
||||||
|
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
user32::SetWindowTextW(self.window.0, text.as_ptr() as winapi::LPCWSTR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn show(&self) {
|
||||||
|
unsafe {
|
||||||
|
user32::ShowWindow(self.window.0, winapi::SW_SHOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn hide(&self) {
|
||||||
|
unsafe {
|
||||||
|
user32::ShowWindow(self.window.0, winapi::SW_HIDE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See the docs in the crate root file.
|
||||||
|
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
let mut placement: winapi::WINDOWPLACEMENT = unsafe { mem::zeroed() };
|
||||||
|
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT;
|
||||||
|
|
||||||
|
if unsafe { user32::GetWindowPlacement(self.window.0, &mut placement) } == 0 {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
let ref rect = placement.rcNormalPosition;
|
||||||
|
Some((rect.left as i32, rect.top as i32))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See the docs in the crate root file.
|
||||||
|
pub fn set_position(&self, x: i32, y: i32) {
|
||||||
|
unsafe {
|
||||||
|
user32::SetWindowPos(self.window.0, ptr::null_mut(), x as raw::c_int, y as raw::c_int,
|
||||||
|
0, 0, winapi::SWP_NOZORDER | winapi::SWP_NOSIZE);
|
||||||
|
user32::UpdateWindow(self.window.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See the docs in the crate root file.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||||
|
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
|
||||||
|
|
||||||
|
if unsafe { user32::GetClientRect(self.window.0, &mut rect) } == 0 {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((
|
||||||
|
(rect.right - rect.left) as u32,
|
||||||
|
(rect.bottom - rect.top) as u32
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See the docs in the crate root file.
|
||||||
|
#[inline]
|
||||||
|
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||||
|
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
|
||||||
|
|
||||||
|
if unsafe { user32::GetWindowRect(self.window.0, &mut rect) } == 0 {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((
|
||||||
|
(rect.right - rect.left) as u32,
|
||||||
|
(rect.bottom - rect.top) as u32
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See the docs in the crate root file.
|
||||||
|
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||||
|
unsafe {
|
||||||
|
// Calculate the outer size based upon the specified inner size
|
||||||
|
let mut rect = winapi::RECT { top: 0, left: 0, bottom: y as winapi::LONG, right: x as winapi::LONG };
|
||||||
|
let dw_style = user32::GetWindowLongA(self.window.0, winapi::GWL_STYLE) as winapi::DWORD;
|
||||||
|
let b_menu = !user32::GetMenu(self.window.0).is_null() as winapi::BOOL;
|
||||||
|
let dw_style_ex = user32::GetWindowLongA(self.window.0, winapi::GWL_EXSTYLE) as winapi::DWORD;
|
||||||
|
user32::AdjustWindowRectEx(&mut rect, dw_style, b_menu, dw_style_ex);
|
||||||
|
let outer_x = (rect.right - rect.left).abs() as raw::c_int;
|
||||||
|
let outer_y = (rect.top - rect.bottom).abs() as raw::c_int;
|
||||||
|
|
||||||
|
user32::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, outer_x, outer_y,
|
||||||
|
winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION | winapi::SWP_NOMOVE);
|
||||||
|
user32::UpdateWindow(self.window.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove
|
||||||
|
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
||||||
|
panic!() // Deprecated function ; we don't care anymore
|
||||||
|
}
|
||||||
|
// TODO: remove
|
||||||
|
pub fn platform_window(&self) -> *mut ::libc::c_void {
|
||||||
|
panic!() // Deprecated function ; we don't care anymore
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `hwnd` of this window.
|
||||||
|
#[inline]
|
||||||
|
pub fn hwnd(&self) -> winapi::HWND {
|
||||||
|
self.window.0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||||
|
let cursor_id = match cursor {
|
||||||
|
MouseCursor::Arrow | MouseCursor::Default => winapi::IDC_ARROW,
|
||||||
|
MouseCursor::Hand => winapi::IDC_HAND,
|
||||||
|
MouseCursor::Crosshair => winapi::IDC_CROSS,
|
||||||
|
MouseCursor::Text | MouseCursor::VerticalText => winapi::IDC_IBEAM,
|
||||||
|
MouseCursor::NotAllowed | MouseCursor::NoDrop => winapi::IDC_NO,
|
||||||
|
MouseCursor::EResize => winapi::IDC_SIZEWE,
|
||||||
|
MouseCursor::NResize => winapi::IDC_SIZENS,
|
||||||
|
MouseCursor::WResize => winapi::IDC_SIZEWE,
|
||||||
|
MouseCursor::SResize => winapi::IDC_SIZENS,
|
||||||
|
MouseCursor::EwResize | MouseCursor::ColResize => winapi::IDC_SIZEWE,
|
||||||
|
MouseCursor::NsResize | MouseCursor::RowResize => winapi::IDC_SIZENS,
|
||||||
|
MouseCursor::Wait | MouseCursor::Progress => winapi::IDC_WAIT,
|
||||||
|
MouseCursor::Help => winapi::IDC_HELP,
|
||||||
|
_ => winapi::IDC_ARROW, // use arrow for the missing cases.
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cur = self.window_state.lock().unwrap();
|
||||||
|
cur.cursor = cursor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: it should be possible to rework this function by using the `execute_in_thread` method
|
||||||
|
// of the events loop.
|
||||||
|
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||||
|
let mut current_state = self.window_state.lock().unwrap();
|
||||||
|
|
||||||
|
let foreground_thread_id = unsafe { user32::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) };
|
||||||
|
let current_thread_id = unsafe { kernel32::GetCurrentThreadId() };
|
||||||
|
|
||||||
|
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 1) };
|
||||||
|
|
||||||
|
let res = match (state, current_state.cursor_state) {
|
||||||
|
(CursorState::Normal, CursorState::Normal) => Ok(()),
|
||||||
|
(CursorState::Hide, CursorState::Hide) => Ok(()),
|
||||||
|
(CursorState::Grab, CursorState::Grab) => Ok(()),
|
||||||
|
|
||||||
|
(CursorState::Hide, CursorState::Normal) => {
|
||||||
|
current_state.cursor_state = CursorState::Hide;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|
||||||
|
(CursorState::Normal, CursorState::Hide) => {
|
||||||
|
current_state.cursor_state = CursorState::Normal;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
|
||||||
|
(CursorState::Grab, CursorState::Normal) | (CursorState::Grab, CursorState::Hide) => {
|
||||||
|
unsafe {
|
||||||
|
let mut rect = mem::uninitialized();
|
||||||
|
if user32::GetClientRect(self.window.0, &mut rect) == 0 {
|
||||||
|
return Err(format!("GetWindowRect failed"));
|
||||||
|
}
|
||||||
|
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.left));
|
||||||
|
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.right));
|
||||||
|
if user32::ClipCursor(&rect) == 0 {
|
||||||
|
return Err(format!("ClipCursor failed"));
|
||||||
|
}
|
||||||
|
current_state.cursor_state = CursorState::Grab;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
(CursorState::Normal, CursorState::Grab) => {
|
||||||
|
unsafe {
|
||||||
|
if user32::ClipCursor(ptr::null()) == 0 {
|
||||||
|
return Err(format!("ClipCursor failed"));
|
||||||
|
}
|
||||||
|
current_state.cursor_state = CursorState::Normal;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 0) };
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn hidpi_factor(&self) -> f32 {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||||
|
let mut point = winapi::POINT {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if user32::ClientToScreen(self.window.0, &mut point) == 0 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if user32::SetCursorPos(point.x, point.y) == 0 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn id(&self) -> WindowId {
|
||||||
|
WindowId(self.window.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Window {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
user32::PostMessageW(self.window.0, winapi::WM_DESTROY, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple wrapper that destroys the window when it is destroyed.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct WindowWrapper(winapi::HWND, winapi::HDC);
|
||||||
|
|
||||||
|
impl Drop for WindowWrapper {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
user32::DestroyWindow(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||||
|
inserter: events_loop::Inserter) -> Result<Window, CreationError> {
|
||||||
|
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// registering the window class
|
||||||
|
let class_name = register_window_class();
|
||||||
|
|
||||||
|
// building a RECT object with coordinates
|
||||||
|
let mut rect = winapi::RECT {
|
||||||
|
left: 0, right: window.dimensions.unwrap_or((1024, 768)).0 as winapi::LONG,
|
||||||
|
top: 0, bottom: window.dimensions.unwrap_or((1024, 768)).1 as winapi::LONG,
|
||||||
|
};
|
||||||
|
|
||||||
|
// switching to fullscreen if necessary
|
||||||
|
// this means adjusting the window's position so that it overlaps the right monitor,
|
||||||
|
// and change the monitor's resolution if necessary
|
||||||
|
if window.monitor.is_some() {
|
||||||
|
let monitor = window.monitor.as_ref().unwrap();
|
||||||
|
try!(switch_to_fullscreen(&mut rect, monitor));
|
||||||
|
}
|
||||||
|
|
||||||
|
// computing the style and extended style of the window
|
||||||
|
let (ex_style, style) = if window.monitor.is_some() || !window.decorations {
|
||||||
|
(winapi::WS_EX_APPWINDOW,
|
||||||
|
//winapi::WS_POPUP is incompatible with winapi::WS_CHILD
|
||||||
|
if pl_attribs.parent.is_some() {
|
||||||
|
winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(winapi::WS_EX_APPWINDOW | winapi::WS_EX_WINDOWEDGE,
|
||||||
|
winapi::WS_OVERLAPPEDWINDOW | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN)
|
||||||
|
};
|
||||||
|
|
||||||
|
// adjusting the window coordinates using the style
|
||||||
|
user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||||
|
|
||||||
|
// creating the real window this time, by using the functions in `extra_functions`
|
||||||
|
let real_window = {
|
||||||
|
let (width, height) = if window.monitor.is_some() || window.dimensions.is_some() {
|
||||||
|
(Some(rect.right - rect.left), Some(rect.bottom - rect.top))
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (x, y) = if window.monitor.is_some() {
|
||||||
|
(Some(rect.left), Some(rect.top))
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut style = if !window.visible {
|
||||||
|
style
|
||||||
|
} else {
|
||||||
|
style | winapi::WS_VISIBLE
|
||||||
|
};
|
||||||
|
|
||||||
|
if pl_attribs.parent.is_some() {
|
||||||
|
style |= winapi::WS_CHILD;
|
||||||
|
}
|
||||||
|
|
||||||
|
let handle = user32::CreateWindowExW(ex_style | winapi::WS_EX_ACCEPTFILES,
|
||||||
|
class_name.as_ptr(),
|
||||||
|
title.as_ptr() as winapi::LPCWSTR,
|
||||||
|
style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN,
|
||||||
|
x.unwrap_or(winapi::CW_USEDEFAULT), y.unwrap_or(winapi::CW_USEDEFAULT),
|
||||||
|
width.unwrap_or(winapi::CW_USEDEFAULT), height.unwrap_or(winapi::CW_USEDEFAULT),
|
||||||
|
pl_attribs.parent.unwrap_or(ptr::null_mut()),
|
||||||
|
ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()),
|
||||||
|
ptr::null_mut());
|
||||||
|
|
||||||
|
if handle.is_null() {
|
||||||
|
return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}",
|
||||||
|
format!("{}", io::Error::last_os_error()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
let hdc = user32::GetDC(handle);
|
||||||
|
if hdc.is_null() {
|
||||||
|
return Err(CreationError::OsError(format!("GetDC function failed: {}",
|
||||||
|
format!("{}", io::Error::last_os_error()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowWrapper(handle, hdc)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set up raw mouse input
|
||||||
|
{
|
||||||
|
let mut rid: winapi::RAWINPUTDEVICE = mem::uninitialized();
|
||||||
|
rid.usUsagePage = winapi::HID_USAGE_PAGE_GENERIC;
|
||||||
|
rid.usUsage = winapi::HID_USAGE_GENERIC_MOUSE;
|
||||||
|
rid.dwFlags = 0;
|
||||||
|
rid.hwndTarget = real_window.0;
|
||||||
|
|
||||||
|
user32::RegisterRawInputDevices(&rid, 1, mem::size_of::<winapi::RAWINPUTDEVICE>() as u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating a mutex to track the current window state
|
||||||
|
let window_state = Arc::new(Mutex::new(events_loop::WindowState {
|
||||||
|
cursor: winapi::IDC_ARROW, // use arrow by default
|
||||||
|
cursor_state: CursorState::Normal,
|
||||||
|
attributes: window.clone(),
|
||||||
|
mouse_in_window: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
inserter.insert(real_window.0, window_state.clone());
|
||||||
|
|
||||||
|
// making the window transparent
|
||||||
|
if window.transparent {
|
||||||
|
let bb = winapi::DWM_BLURBEHIND {
|
||||||
|
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
||||||
|
fEnable: 1,
|
||||||
|
hRgnBlur: ptr::null_mut(),
|
||||||
|
fTransitionOnMaximized: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
dwmapi::DwmEnableBlurBehindWindow(real_window.0, &bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calling SetForegroundWindow if fullscreen
|
||||||
|
if window.monitor.is_some() {
|
||||||
|
user32::SetForegroundWindow(real_window.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Building the struct.
|
||||||
|
Ok(Window {
|
||||||
|
window: real_window,
|
||||||
|
window_state: window_state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn register_window_class() -> Vec<u16> {
|
||||||
|
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let class = winapi::WNDCLASSEXW {
|
||||||
|
cbSize: mem::size_of::<winapi::WNDCLASSEXW>() as winapi::UINT,
|
||||||
|
style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC,
|
||||||
|
lpfnWndProc: Some(events_loop::callback),
|
||||||
|
cbClsExtra: 0,
|
||||||
|
cbWndExtra: 0,
|
||||||
|
hInstance: kernel32::GetModuleHandleW(ptr::null()),
|
||||||
|
hIcon: ptr::null_mut(),
|
||||||
|
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
||||||
|
hbrBackground: ptr::null_mut(),
|
||||||
|
lpszMenuName: ptr::null(),
|
||||||
|
lpszClassName: class_name.as_ptr(),
|
||||||
|
hIconSm: ptr::null_mut(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// We ignore errors because registering the same window class twice would trigger
|
||||||
|
// an error, and because errors here are detected during CreateWindowEx anyway.
|
||||||
|
// Also since there is no weird element in the struct, there is no reason for this
|
||||||
|
// call to fail.
|
||||||
|
user32::RegisterClassExW(&class);
|
||||||
|
|
||||||
|
class_name
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn switch_to_fullscreen(rect: &mut winapi::RECT, monitor: &MonitorId)
|
||||||
|
-> Result<(), CreationError>
|
||||||
|
{
|
||||||
|
// adjusting the rect
|
||||||
|
{
|
||||||
|
let pos = monitor.get_position();
|
||||||
|
rect.left += pos.0 as winapi::LONG;
|
||||||
|
rect.right += pos.0 as winapi::LONG;
|
||||||
|
rect.top += pos.1 as winapi::LONG;
|
||||||
|
rect.bottom += pos.1 as winapi::LONG;
|
||||||
|
}
|
||||||
|
|
||||||
|
// changing device settings
|
||||||
|
let mut screen_settings: winapi::DEVMODEW = mem::zeroed();
|
||||||
|
screen_settings.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD;
|
||||||
|
screen_settings.dmPelsWidth = (rect.right - rect.left) as winapi::DWORD;
|
||||||
|
screen_settings.dmPelsHeight = (rect.bottom - rect.top) as winapi::DWORD;
|
||||||
|
screen_settings.dmBitsPerPel = 32; // TODO: ?
|
||||||
|
screen_settings.dmFields = winapi::DM_BITSPERPEL | winapi::DM_PELSWIDTH | winapi::DM_PELSHEIGHT;
|
||||||
|
|
||||||
|
let result = user32::ChangeDisplaySettingsExW(monitor.get_adapter_name().as_ptr(),
|
||||||
|
&mut screen_settings, ptr::null_mut(),
|
||||||
|
winapi::CDS_FULLSCREEN, ptr::null_mut());
|
||||||
|
|
||||||
|
if result != winapi::DISP_CHANGE_SUCCESSFUL {
|
||||||
|
return Err(CreationError::OsError(format!("ChangeDisplaySettings failed: {}", result)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -320,6 +320,14 @@ impl Iterator for AvailableMonitorsIter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the list of all available monitors.
|
/// Returns the list of all available monitors.
|
||||||
|
///
|
||||||
|
/// Usage will result in display backend initialisation, this can be controlled on linux
|
||||||
|
/// using an environment variable `WINIT_UNIX_BACKEND`.
|
||||||
|
/// > Legal values are `x11` and `wayland`. If this variable is set only the named backend
|
||||||
|
/// > will be tried by winit. If it is not set, winit will try to connect to a wayland connection,
|
||||||
|
/// > and if it fails will fallback on x11.
|
||||||
|
/// >
|
||||||
|
/// > If this variable is set with any other value, winit will panic.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_available_monitors() -> AvailableMonitorsIter {
|
pub fn get_available_monitors() -> AvailableMonitorsIter {
|
||||||
let data = platform::get_available_monitors();
|
let data = platform::get_available_monitors();
|
||||||
|
@ -327,6 +335,14 @@ pub fn get_available_monitors() -> AvailableMonitorsIter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the primary monitor of the system.
|
/// Returns the primary monitor of the system.
|
||||||
|
///
|
||||||
|
/// Usage will result in display backend initialisation, this can be controlled on linux
|
||||||
|
/// using an environment variable `WINIT_UNIX_BACKEND`.
|
||||||
|
/// > Legal values are `x11` and `wayland`. If this variable is set only the named backend
|
||||||
|
/// > will be tried by winit. If it is not set, winit will try to connect to a wayland connection,
|
||||||
|
/// > and if it fails will fallback on x11.
|
||||||
|
/// >
|
||||||
|
/// > If this variable is set with any other value, winit will panic.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_primary_monitor() -> MonitorId {
|
pub fn get_primary_monitor() -> MonitorId {
|
||||||
MonitorId(platform::get_primary_monitor())
|
MonitorId(platform::get_primary_monitor())
|
||||||
|
|
Loading…
Reference in a new issue