From 9804cad7dd86a3661bc9f3996f8fc1a84e7af5ab Mon Sep 17 00:00:00 2001 From: Alex Butler Date: Wed, 21 Jun 2017 18:34:16 +0100 Subject: [PATCH 01/15] Allow usage of XWayland Will prefer X11 over wayland when the environment variable `WINIT_PREFER_UNIX_BACKEND=x11` is set. --- src/platform/linux/mod.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 6bc4719d..f914a2b0 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -2,6 +2,7 @@ use std::collections::VecDeque; use std::sync::Arc; +use std::env; use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow}; use libc; @@ -15,6 +16,10 @@ mod dlopen; pub mod wayland; pub mod x11; +/// Environment variable that indicates the X11 backend should be used +/// even if Wayland is available. In this case XWayland will be used. +const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_PREFER_UNIX_BACKEND"; + #[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes { pub visual_infos: Option, @@ -25,18 +30,30 @@ pub enum UnixBackend { X(Arc), Wayland(Arc), Error(XNotSupported), -} +} lazy_static!( pub static ref UNIX_BACKEND: UnixBackend = { - if let Some(ctxt) = wayland::WaylandContext::init() { - UnixBackend::Wayland(Arc::new(ctxt)) - } else { + #[inline] + fn x_backend() -> UnixBackend { match XConnection::new(Some(x_error_callback)) { Ok(x) => UnixBackend::X(Arc::new(x)), Err(e) => UnixBackend::Error(e), } } + match env::var(BACKEND_PREFERENCE_ENV_VAR) { + Ok(ref s) if s == "x11" => { + println!("{}: using x11 backend", BACKEND_PREFERENCE_ENV_VAR); + x_backend() + }, + _ => { + if let Some(ctxt) = wayland::WaylandContext::init() { + UnixBackend::Wayland(Arc::new(ctxt)) + } else { + x_backend() + } + }, + } }; ); From 7a19ef19071df84fdc47bc9e4f4438373cac44ae Mon Sep 17 00:00:00 2001 From: Alex Butler Date: Wed, 21 Jun 2017 19:41:26 +0100 Subject: [PATCH 02/15] Make usage of env vars strict Using `BACKEND_PREFERENCE_ENV_VAR=$backend` will no longer fallback on any other backend --- src/platform/linux/mod.rs | 45 ++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index f914a2b0..9daae7d4 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -29,29 +29,40 @@ pub struct PlatformSpecificWindowBuilderAttributes { pub enum UnixBackend { X(Arc), Wayland(Arc), - Error(XNotSupported), + Error(Option, Option), } lazy_static!( pub static ref UNIX_BACKEND: UnixBackend = { #[inline] - fn x_backend() -> UnixBackend { + fn x_backend() -> Result { match XConnection::new(Some(x_error_callback)) { - Ok(x) => UnixBackend::X(Arc::new(x)), - Err(e) => UnixBackend::Error(e), + Ok(x) => Ok(UnixBackend::X(Arc::new(x))), + Err(e) => Err(e), } } + #[inline] + fn wayland_backend() -> Result { + wayland::WaylandContext::init() + .map(|ctx| UnixBackend::Wayland(Arc::new(ctx))) + .ok_or(()) + } match env::var(BACKEND_PREFERENCE_ENV_VAR) { - Ok(ref s) if s == "x11" => { - println!("{}: using x11 backend", BACKEND_PREFERENCE_ENV_VAR); - x_backend() + 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), }, - _ => { - if let Some(ctxt) = wayland::WaylandContext::init() { - UnixBackend::Wayland(Arc::new(ctxt)) - } else { - x_backend() - } + 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())) + }) + }) }, } }; @@ -102,7 +113,7 @@ pub fn get_available_monitors() -> VecDeque { .into_iter() .map(MonitorId::X) .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}, } } @@ -111,7 +122,7 @@ pub fn get_primary_monitor() -> MonitorId { match *UNIX_BACKEND { UnixBackend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)), UnixBackend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)), - UnixBackend::Error(_) => MonitorId::None, + UnixBackend::Error(..) => MonitorId::None, } } @@ -164,7 +175,7 @@ impl Window2 { UnixBackend::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, // thus this function cannot be called! unreachable!() @@ -341,7 +352,7 @@ impl EventsLoop { EventsLoop::X(x11::EventsLoop::new(ctxt.clone())) }, - UnixBackend::Error(_) => { + UnixBackend::Error(..) => { panic!("Attempted to create an EventsLoop while no backend was available.") } } From 789598fa84256b7a38ebfdaabb0a33c9c65e97f5 Mon Sep 17 00:00:00 2001 From: Alex Butler Date: Wed, 21 Jun 2017 19:54:21 +0100 Subject: [PATCH 03/15] Rename unix backend env var Rename inline with stricter behaviour Add docs explaining behaviour --- src/platform/linux/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 9daae7d4..2b8e53cd 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -16,9 +16,12 @@ mod dlopen; pub mod wayland; pub mod x11; -/// Environment variable that indicates the X11 backend should be used -/// even if Wayland is available. In this case XWayland will be used. -const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_PREFER_UNIX_BACKEND"; +/// Environment variable that indicates a backend preference +/// `WINIT_UNIX_BACKEND=x11` : Will try to use an X11 backend +/// `WINIT_UNIX_BACKEND=wayland` : Will try to use a wayland backend +/// When used is selected backends will not fallback to one another +/// (Current default behaviour is wayland, fallback to X) +const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND"; #[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes { From eaa92a428263832d01ebd8cf1b486b5cc0b2d708 Mon Sep 17 00:00:00 2001 From: Alex Butler Date: Wed, 21 Jun 2017 19:59:56 +0100 Subject: [PATCH 04/15] Improve unix backend env var docs --- src/platform/linux/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 2b8e53cd..909ca1e3 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -19,8 +19,8 @@ pub mod x11; /// Environment variable that indicates a backend preference /// `WINIT_UNIX_BACKEND=x11` : Will try to use an X11 backend /// `WINIT_UNIX_BACKEND=wayland` : Will try to use a wayland backend -/// When used is selected backends will not fallback to one another -/// (Current default behaviour is wayland, fallback to X) +/// When the variable is present the indicated backend will not fallback to another +/// (Current default behaviour, without the env var, is try wayland then fallback to X) const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND"; #[derive(Clone, Default)] From 3a898437671796e865db8b7ba37dd90ce8f2fbed Mon Sep 17 00:00:00 2001 From: Alex Butler Date: Wed, 21 Jun 2017 20:10:23 +0100 Subject: [PATCH 05/15] Add `WINIT_UNIX_BACKEND` documentation --- src/lib.rs | 10 +++++++++- src/platform/linux/mod.rs | 12 +++++++----- src/window.rs | 20 ++++++++++++++++++-- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d4b91c40..291bfb37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//! Winit allows you to build a window on as many platforms as possible. +//! Winit allows you to build a window on as many platforms as possible. //! //! # Building a window //! @@ -181,6 +181,14 @@ pub struct ButtonId(u32); /// 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. +/// +/// 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 { events_loop: platform::EventsLoop, } diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 909ca1e3..af431a10 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -16,11 +16,13 @@ mod dlopen; pub mod wayland; pub mod x11; -/// Environment variable that indicates a backend preference -/// `WINIT_UNIX_BACKEND=x11` : Will try to use an X11 backend -/// `WINIT_UNIX_BACKEND=wayland` : Will try to use a wayland backend -/// When the variable is present the indicated backend will not fallback to another -/// (Current default behaviour, without the env var, is try wayland then fallback to X) +/// 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)] diff --git a/src/window.rs b/src/window.rs index 94c677b0..d93ee470 100644 --- a/src/window.rs +++ b/src/window.rs @@ -30,7 +30,7 @@ impl WindowBuilder { self.window.dimensions = Some((width, height)); self } - + /// Sets a minimum dimension size for the window /// /// Width and height are in pixels. @@ -197,7 +197,7 @@ impl Window { pub fn get_inner_size(&self) -> Option<(u32, u32)> { self.window.get_inner_size() } - + /// Returns the size in points of the client area of the window. /// /// The client area is the content of the window, excluding the title bar and borders. @@ -320,6 +320,14 @@ impl Iterator for AvailableMonitorsIter { } /// 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] pub fn get_available_monitors() -> AvailableMonitorsIter { let data = platform::get_available_monitors(); @@ -327,6 +335,14 @@ pub fn get_available_monitors() -> AvailableMonitorsIter { } /// 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] pub fn get_primary_monitor() -> MonitorId { MonitorId(platform::get_primary_monitor()) From 66a12d5332b1466c0c7f1a4cc7e93af24e65ca92 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 23 Jun 2017 20:20:49 +0200 Subject: [PATCH 06/15] linux: stop exposing wayland_client's types --- src/os/unix.rs | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/src/os/unix.rs b/src/os/unix.rs index 9d897a76..7abac2de 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -10,9 +10,6 @@ use WindowBuilder; use platform::x11::XConnection; use platform::x11::ffi::XVisualInfo; -use wayland_client::protocol::wl_display::WlDisplay; -use wayland_client::protocol::wl_surface::WlSurface; - pub use platform::x11; // TODO: do not expose XConnection @@ -63,24 +60,6 @@ pub trait WindowExt { /// /// The pointer will become invalid when the glutin `Window` is destroyed. 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 { @@ -124,28 +103,17 @@ impl WindowExt for Window { #[inline] fn get_wayland_surface(&self) -> Option<*mut libc::c_void> { 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 { - LinuxWindow::Wayland(ref w) => Some(w.get_surface()), + LinuxWindow::Wayland(ref w) => Some(w.get_surface().ptr() as *mut _), _ => None } } #[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 { - LinuxWindow::Wayland(ref w) => Some(w.get_display()), + LinuxWindow::Wayland(ref w) => Some(w.get_display().ptr() as *mut _), _ => None } } From e915454a9d8e4485ea0dfe53d981ac2ff027694c Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Fri, 23 Jun 2017 20:25:06 +0200 Subject: [PATCH 07/15] linux: update wayland dependencies --- Cargo.toml | 6 +++--- src/lib.rs | 2 +- src/platform/linux/wayland/event_loop.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 164f9562..e4de0727 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,8 +37,8 @@ kernel32-sys = "0.2" dwmapi-sys = "0.1" [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-kbd = "0.8.0" -wayland-window = "0.5.0" +wayland-client = { version = "0.9.9", features = ["dlopen"] } +wayland-kbd = "0.9.1" +wayland-window = "0.6.1" tempfile = "2.1" x11-dl = "2.8" diff --git a/src/lib.rs b/src/lib.rs index d4b91c40..03825e61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,7 +114,7 @@ extern crate core_graphics; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))] extern crate x11_dl; #[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; pub use events::*; diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform/linux/wayland/event_loop.rs index 703562b6..a3e72edf 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform/linux/wayland/event_loop.rs @@ -7,7 +7,7 @@ use std::sync::atomic::{self, AtomicBool}; 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 super::make_wid; @@ -159,13 +159,13 @@ impl EventsLoop { } 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 state = evq_guard.state(); let handler = state.get_mut_handler::(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 w.is_alive() { + if w.status() == Liveness::Alive { handler.mouse_focus = Some(w) } } From 9a17c2eb8697d9931761bec10663675b309d4782 Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sat, 24 Jun 2017 12:26:15 +1000 Subject: [PATCH 08/15] Publish version 0.7.0 Includes: - Recent removal of sync (breaking change) #191. - Wayland fixes: #190, #188, #181 - X11 fixes: #174, #178, --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 164f9562..3026f1e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winit" -version = "0.6.4" +version = "0.7.0" authors = ["The winit contributors, Pierre Krieger "] description = "Cross-platform window creation library." keywords = ["windowing"] From 9d2a32b4152fc74574256852f3eaf6e6201b647f Mon Sep 17 00:00:00 2001 From: mitchmindtree Date: Sat, 24 Jun 2017 17:08:05 +1000 Subject: [PATCH 09/15] Update the README for the version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 868486de..3e82f258 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ```toml [dependencies] -winit = "0.6" +winit = "0.7" ``` ## [Documentation](https://docs.rs/winit) From 61ba6dce7fa369506b785f1ce037378784a30a7c Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 26 Jun 2017 21:21:13 +0200 Subject: [PATCH 10/15] Switch win32 implementation to new design --- src/os/windows.rs | 2 +- src/platform/windows/callback.rs | 426 ----------------- src/platform/windows/events_loop.rs | 700 ++++++++++++++++++++++++++++ src/platform/windows/init.rs | 259 ---------- src/platform/windows/mod.rs | 387 +-------------- src/platform/windows/window.rs | 483 +++++++++++++++++++ 6 files changed, 1203 insertions(+), 1054 deletions(-) delete mode 100644 src/platform/windows/callback.rs create mode 100644 src/platform/windows/events_loop.rs delete mode 100644 src/platform/windows/init.rs create mode 100644 src/platform/windows/window.rs diff --git a/src/os/windows.rs b/src/os/windows.rs index 6d8b7981..9b52c2de 100644 --- a/src/os/windows.rs +++ b/src/os/windows.rs @@ -18,7 +18,7 @@ pub trait WindowExt { impl WindowExt for Window { #[inline] fn get_hwnd(&self) -> *mut libc::c_void { - self.window.platform_window() + self.window.hwnd() as *mut _ } } diff --git a/src/platform/windows/callback.rs b/src/platform/windows/callback.rs deleted file mode 100644 index c11aac13..00000000 --- a/src/platform/windows/callback.rs +++ /dev/null @@ -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> = RefCell::new(None)); - -pub struct ThreadLocalData { - pub win: winapi::HWND, - pub sender: Sender, - pub window_state: Arc>, - 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::() 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::() as winapi::UINT; - user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT, - mem::transmute(&mut data), &mut data_size, - mem::size_of::() 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); diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs new file mode 100644 index 00000000..beb756d5 --- /dev/null +++ b/src/platform/windows/events_loop.rs @@ -0,0 +1,700 @@ +//! 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; + +/// 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>) { + 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, +} + +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::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(&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(&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(&self, function: F) + where F: FnMut(Inserter) + Send + 'static + { + unsafe { + let boxed = Box::new(function) as Box; + 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> 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> = RefCell::new(None)); +struct ThreadLocalData { + sender: mpsc::Sender, + windows: HashMap>>, +} + +// 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::() 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 => { + let mut data: winapi::RAWINPUT = mem::uninitialized(); + let mut data_size = mem::size_of::() as winapi::UINT; + user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT, + mem::transmute(&mut data), &mut data_size, + mem::size_of::() 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(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) + } + } +} diff --git a/src/platform/windows/init.rs b/src/platform/windows/init.rs deleted file mode 100644 index 2d3b1537..00000000 --- a/src/platform/windows/init.rs +++ /dev/null @@ -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 { - 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::>(); - - 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, window: &WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result { - // 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 { - let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter()) - .collect::>(); - - let class = winapi::WNDCLASSEXW { - cbSize: mem::size_of::() 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::() 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(()) -} diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index fe6ac385..52e77525 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -1,21 +1,10 @@ #![cfg(target_os = "windows")] -use std::mem; -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 winapi; -use WindowAttributes; - -gen_api_transition!(); +pub use self::events_loop::{EventsLoop, EventsLoopProxy}; +pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor}; +pub use self::window::Window; #[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes { @@ -25,361 +14,23 @@ pub struct PlatformSpecificWindowBuilderAttributes { unsafe impl Send for PlatformSpecificWindowBuilderAttributes {} unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {} -#[derive(Clone, Default)] -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 +// TODO: document what this means pub type Cursor = *const winapi::wchar_t; -/// Contains information about states and the window for the callback. -#[derive(Clone)] -pub struct WindowState { - pub cursor: Cursor, - pub cursor_state: CursorState, - pub attributes: WindowAttributes -} +// TODO: remove +pub type Window2 = Window; -/// The Win32 implementation of the main `Window` object. -pub struct Window { - /// Main handle for the window. - window: WindowWrapper, +// Constant device ID, to be removed when this backend is updated to report real device IDs. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DeviceId; +const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId); - /// Receiver for the events dispatched by the window callback. - events_receiver: Receiver, +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WindowId(winapi::HWND); +unsafe impl Send for WindowId {} +unsafe impl Sync for WindowId {} - /// The current window state. - window_state: Arc>, -} - -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 - { - 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::>(); - - 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::() 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) { - } - - #[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 { - 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 { - self.window.events_receiver.recv().ok() - } -} +mod event; +mod events_loop; +mod monitor; +mod window; diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs new file mode 100644 index 00000000..fc08a5d3 --- /dev/null +++ b/src/platform/windows/window.rs @@ -0,0 +1,483 @@ +#![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>, +} + +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 + { + 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::>(); + + 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::() 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 { + let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter()) + .collect::>(); + + // 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) + }; + + // 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 { + let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter()) + .collect::>(); + + let class = winapi::WNDCLASSEXW { + cbSize: mem::size_of::() 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::() 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(()) +} From 886eab5c7ee26cb2535b7a46050a628411561ae9 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sat, 1 Jul 2017 16:47:53 +1000 Subject: [PATCH 11/15] Fix build on nightly Nightly recently fixed some soundness issues related to `Sized`. This had to be fixed in the cocoa crates as well, and also affects our ios bindings. --- Cargo.toml | 6 +++--- src/platform/ios/ffi.rs | 2 +- src/platform/macos/window.rs | 18 +++++++----------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 174936f4..022c0fd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,9 +24,9 @@ objc = "0.2" [target.'cfg(target_os = "macos")'.dependencies] objc = "0.2" cgl = "0.2" -cocoa = "=0.5.2" -core-foundation = "0.2" -core-graphics = "0.4" +cocoa = "0.9" +core-foundation = "0.4" +core-graphics = "0.8" [target.'cfg(target_os = "windows")'.dependencies] winapi = "0.2" diff --git a/src/platform/ios/ffi.rs b/src/platform/ios/ffi.rs index 42a946d9..cadc5623 100644 --- a/src/platform/ios/ffi.rs +++ b/src/platform/ios/ffi.rs @@ -66,7 +66,7 @@ extern { pub fn longjmp(env: *mut libc::c_void, val: libc::c_int); } -pub trait NSString { +pub trait NSString: Sized { unsafe fn alloc(_: Self) -> id { msg_send![class("NSString"), alloc] } diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index 8ea04a76..cbd66056 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -338,21 +338,17 @@ impl Window { let masks = if screen.is_some() { // Fullscreen window - appkit::NSBorderlessWindowMask as NSUInteger | - appkit::NSResizableWindowMask as NSUInteger | - appkit::NSTitledWindowMask as NSUInteger + appkit::NSBorderlessWindowMask | appkit::NSResizableWindowMask | + appkit::NSTitledWindowMask } else if attrs.decorations { // Window with a titlebar - appkit::NSClosableWindowMask as NSUInteger | - appkit::NSMiniaturizableWindowMask as NSUInteger | - appkit::NSResizableWindowMask as NSUInteger | - appkit::NSTitledWindowMask as NSUInteger + appkit::NSClosableWindowMask | appkit::NSMiniaturizableWindowMask | + appkit::NSResizableWindowMask | appkit::NSTitledWindowMask } else { // Window without a titlebar - appkit::NSClosableWindowMask as NSUInteger | - appkit::NSMiniaturizableWindowMask as NSUInteger | - appkit::NSResizableWindowMask as NSUInteger | - appkit::NSFullSizeContentViewWindowMask as NSUInteger + appkit::NSClosableWindowMask | appkit::NSMiniaturizableWindowMask | + appkit::NSResizableWindowMask | + appkit::NSFullSizeContentViewWindowMask }; let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( From e196f80e988cb06012e67910c048db12bb91960d Mon Sep 17 00:00:00 2001 From: tomaka Date: Sat, 1 Jul 2017 10:28:37 +0200 Subject: [PATCH 12/15] Publish 0.7.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 022c0fd3..d3e8a9c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winit" -version = "0.7.0" +version = "0.7.1" authors = ["The winit contributors, Pierre Krieger "] description = "Cross-platform window creation library." keywords = ["windowing"] From ac0d6c890a5eed449ca80b50841d31ba45e52230 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Wed, 5 Jul 2017 09:13:39 +0200 Subject: [PATCH 13/15] Remove dependency on cgl on OSX --- Cargo.toml | 1 - src/lib.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d3e8a9c0..a0569f7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ objc = "0.2" [target.'cfg(target_os = "macos")'.dependencies] objc = "0.2" -cgl = "0.2" cocoa = "0.9" core-foundation = "0.4" core-graphics = "0.8" diff --git a/src/lib.rs b/src/lib.rs index 55e92c4e..eed9391a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,8 +104,6 @@ extern crate dwmapi; #[macro_use] extern crate objc; #[cfg(target_os = "macos")] -extern crate cgl; -#[cfg(target_os = "macos")] extern crate cocoa; #[cfg(target_os = "macos")] extern crate core_foundation; From 0371b6573f3791e2dbd8acd1cbdf93998e837fd9 Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Tue, 4 Jul 2017 19:32:59 -0700 Subject: [PATCH 14/15] Implement raw mouse motion for Windows --- src/platform/windows/events_loop.rs | 25 ++++++++++++++++++++----- src/platform/windows/window.rs | 11 +++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs index beb756d5..48b56e75 100644 --- a/src/platform/windows/events_loop.rs +++ b/src/platform/windows/events_loop.rs @@ -42,6 +42,7 @@ 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)] @@ -556,6 +557,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, }, winapi::WM_INPUT => { + use events::DeviceEvent::Motion; let mut data: winapi::RAWINPUT = mem::uninitialized(); let mut data_size = mem::size_of::() as winapi::UINT; user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT, @@ -563,13 +565,26 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, mem::size_of::() 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 }); + 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) } diff --git a/src/platform/windows/window.rs b/src/platform/windows/window.rs index fc08a5d3..250b773c 100644 --- a/src/platform/windows/window.rs +++ b/src/platform/windows/window.rs @@ -389,6 +389,17 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild 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::() 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 From 5ebeb8ab5f8c4cecaca5b85231106c13abd84444 Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Thu, 6 Jul 2017 23:33:42 +0200 Subject: [PATCH 15/15] Fix Android compilation error --- Cargo.toml | 2 +- src/platform/android/mod.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a0569f7f..8d278a64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "winit" -version = "0.7.1" +version = "0.7.2" authors = ["The winit contributors, Pierre Krieger "] description = "Cross-platform window creation library." keywords = ["windowing"] diff --git a/src/platform/android/mod.rs b/src/platform/android/mod.rs index dec6e752..09c9cb14 100644 --- a/src/platform/android/mod.rs +++ b/src/platform/android/mod.rs @@ -82,6 +82,7 @@ impl<'a> Iterator for PollEventsIterator<'a> { }, location: (motion.x as f64, motion.y as f64), id: motion.pointer_id as u64, + device_id: DEVICE_ID, })) }, Ok(android_glue::Event::InitWindow) => { @@ -273,3 +274,6 @@ impl WindowProxy { 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);