mirror of
https://github.com/rust-windowing/winit.git
synced 2026-08-31 05:40:04 -04:00
Add ability to send custom user events
This commit is contained in:
@@ -35,6 +35,9 @@ cocoa = "0.18.4"
|
|||||||
core-foundation = "0.6"
|
core-foundation = "0.6"
|
||||||
core-graphics = "0.17.3"
|
core-graphics = "0.17.3"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies.crossbeam-channel]
|
||||||
|
version = "0.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
features = [
|
features = [
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ another library.
|
|||||||
extern crate winit;
|
extern crate winit;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut events_loop = winit::EventsLoop::new();
|
let mut event_loop = winit::EventLoop::new();
|
||||||
let window = winit::Window::new(&events_loop).unwrap();
|
let window = winit::Window::new(&event_loop).unwrap();
|
||||||
|
|
||||||
events_loop.run_forever(|event| {
|
event_loop.run(|event| {
|
||||||
match event {
|
match event {
|
||||||
winit::Event::WindowEvent {
|
winit::Event::WindowEvent {
|
||||||
event: winit::WindowEvent::CloseRequested,
|
event: winit::WindowEvent::CloseRequested,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
extern crate winit;
|
extern crate winit;
|
||||||
|
use winit::{EventLoop, WindowBuilder};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let events_loop = winit::EventLoop::new();
|
let events_loop: EventLoop<i32> = EventLoop::new_user_event();
|
||||||
|
|
||||||
let _window = winit::WindowBuilder::new()
|
let _window = WindowBuilder::new()
|
||||||
.with_title("A fantastic window!")
|
.with_title("A fantastic window!")
|
||||||
.build(&events_loop)
|
.build(&events_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -11,10 +12,12 @@ fn main() {
|
|||||||
let proxy = events_loop.create_proxy();
|
let proxy = events_loop.create_proxy();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
let mut counter = 0;
|
||||||
// Wake up the `events_loop` once every second.
|
// Wake up the `events_loop` once every second.
|
||||||
loop {
|
loop {
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
proxy.wakeup().unwrap();
|
proxy.send_event(counter).unwrap();
|
||||||
|
counter += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -22,7 +25,7 @@ fn main() {
|
|||||||
println!("{:?}", event);
|
println!("{:?}", event);
|
||||||
match event {
|
match event {
|
||||||
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } =>
|
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } =>
|
||||||
*control_flow = winit::ControlFlow::Wait,
|
*control_flow = winit::ControlFlow::Exit,
|
||||||
_ => *control_flow = winit::ControlFlow::Wait,
|
_ => *control_flow = winit::ControlFlow::Wait,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use {DeviceId, LogicalPosition, LogicalSize, WindowId};
|
|||||||
|
|
||||||
/// Describes a generic event.
|
/// Describes a generic event.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Event {
|
pub enum Event<T> {
|
||||||
WindowEvent {
|
WindowEvent {
|
||||||
window_id: WindowId,
|
window_id: WindowId,
|
||||||
event: WindowEvent,
|
event: WindowEvent,
|
||||||
@@ -14,7 +14,7 @@ pub enum Event {
|
|||||||
device_id: DeviceId,
|
device_id: DeviceId,
|
||||||
event: DeviceEvent,
|
event: DeviceEvent,
|
||||||
},
|
},
|
||||||
Awakened,
|
UserEvent(T),
|
||||||
/// Emitted when new events arrive from the OS to be processed.
|
/// Emitted when new events arrive from the OS to be processed.
|
||||||
NewEvents(StartCause),
|
NewEvents(StartCause),
|
||||||
/// Emitted when all of the event loop's events have been processed and control flow is about
|
/// Emitted when all of the event loop's events have been processed and control flow is about
|
||||||
@@ -31,6 +31,21 @@ pub enum Event {
|
|||||||
Suspended(bool),
|
Suspended(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Event<T> {
|
||||||
|
pub fn map_nonuser_event<U>(self) -> Result<Event<U>, Event<T>> {
|
||||||
|
use self::Event::*;
|
||||||
|
match self {
|
||||||
|
UserEvent(_) => Err(self),
|
||||||
|
WindowEvent{window_id, event} => Ok(WindowEvent{window_id, event}),
|
||||||
|
DeviceEvent{device_id, event} => Ok(DeviceEvent{device_id, event}),
|
||||||
|
NewEvents(cause) => Ok(NewEvents(cause)),
|
||||||
|
EventsCleared => Ok(EventsCleared),
|
||||||
|
LoopDestroyed => Ok(LoopDestroyed),
|
||||||
|
Suspended(suspended) => Ok(Suspended(suspended)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum StartCause {
|
pub enum StartCause {
|
||||||
/// Sent if the time specified by `ControlFlow::WaitTimeout` has been elapsed. Contains the
|
/// Sent if the time specified by `ControlFlow::WaitTimeout` has been elapsed. Contains the
|
||||||
|
|||||||
41
src/lib.rs
41
src/lib.rs
@@ -72,6 +72,9 @@ extern crate serde;
|
|||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
extern crate winapi;
|
extern crate winapi;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate crossbeam_channel;
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate objc;
|
extern crate objc;
|
||||||
@@ -163,12 +166,12 @@ pub struct DeviceId(platform::DeviceId);
|
|||||||
/// forbiding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the
|
/// forbiding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the
|
||||||
/// `Window` created from this `EventLoop` _can_ be sent to an other thread, and the
|
/// `Window` created from this `EventLoop` _can_ be sent to an other thread, and the
|
||||||
/// `EventLoopProxy` allows you to wakeup an `EventLoop` from an other thread.
|
/// `EventLoopProxy` allows you to wakeup an `EventLoop` from an other thread.
|
||||||
pub struct EventLoop {
|
pub struct EventLoop<T> {
|
||||||
events_loop: platform::EventLoop,
|
events_loop: platform::EventLoop<T>,
|
||||||
_marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync
|
_marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for EventLoop {
|
impl<T> std::fmt::Debug for EventLoop<T> {
|
||||||
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
fmtr.pad("EventLoop { .. }")
|
fmtr.pad("EventLoop { .. }")
|
||||||
}
|
}
|
||||||
@@ -199,14 +202,20 @@ impl Default for ControlFlow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventLoop {
|
impl EventLoop<()> {
|
||||||
|
pub fn new() -> EventLoop<()> {
|
||||||
|
EventLoop::<()>::new_user_event()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoop<T> {
|
||||||
/// Builds a new events loop.
|
/// Builds a new events loop.
|
||||||
///
|
///
|
||||||
/// Usage will result in display backend initialisation, this can be controlled on linux
|
/// 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`.
|
/// using an environment variable `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`.
|
||||||
/// If it is not set, winit will try to connect to a wayland connection, and if it fails will
|
/// 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.
|
/// fallback on x11. If this variable is set with any other value, winit will panic.
|
||||||
pub fn new() -> EventLoop {
|
pub fn new_user_event() -> EventLoop<T> {
|
||||||
EventLoop {
|
EventLoop {
|
||||||
events_loop: platform::EventLoop::new(),
|
events_loop: platform::EventLoop::new(),
|
||||||
_marker: ::std::marker::PhantomData,
|
_marker: ::std::marker::PhantomData,
|
||||||
@@ -234,14 +243,14 @@ impl EventLoop {
|
|||||||
/// Any values not passed to this function will *not* be dropped.
|
/// Any values not passed to this function will *not* be dropped.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn run<F>(self, event_handler: F) -> !
|
pub fn run<F>(self, event_handler: F) -> !
|
||||||
where F: 'static + FnMut(Event, &EventLoop, &mut ControlFlow)
|
where F: 'static + FnMut(Event<T>, &EventLoop<T>, &mut ControlFlow)
|
||||||
{
|
{
|
||||||
self.events_loop.run(event_handler)
|
self.events_loop.run(event_handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an `EventLoopProxy` that can be used to wake up the `EventLoop` from another
|
/// Creates an `EventLoopProxy` that can be used to wake up the `EventLoop` from another
|
||||||
/// thread.
|
/// thread.
|
||||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||||
EventLoopProxy {
|
EventLoopProxy {
|
||||||
events_loop_proxy: self.events_loop.create_proxy(),
|
events_loop_proxy: self.events_loop.create_proxy(),
|
||||||
}
|
}
|
||||||
@@ -250,24 +259,24 @@ impl EventLoop {
|
|||||||
|
|
||||||
/// Used to wake up the `EventLoop` from another thread.
|
/// Used to wake up the `EventLoop` from another thread.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EventLoopProxy {
|
pub struct EventLoopProxy<T> {
|
||||||
events_loop_proxy: platform::EventLoopProxy,
|
events_loop_proxy: platform::EventLoopProxy<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for EventLoopProxy {
|
impl<T> std::fmt::Debug for EventLoopProxy<T> {
|
||||||
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
fmtr.pad("EventLoopProxy { .. }")
|
fmtr.pad("EventLoopProxy { .. }")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventLoopProxy {
|
impl<T> EventLoopProxy<T> {
|
||||||
/// Wake up the `EventLoop` from which this proxy was created.
|
/// Send an event to the `EventLoop` from which this proxy was created. This emits a
|
||||||
///
|
/// `UserEvent(event)` event in the event loop, where `event` is the value passed to this
|
||||||
/// This causes the `EventLoop` to emit an `Awakened` event.
|
/// function.
|
||||||
///
|
///
|
||||||
/// Returns an `Err` if the associated `EventLoop` no longer exists.
|
/// Returns an `Err` if the associated `EventLoop` no longer exists.
|
||||||
pub fn wakeup(&self) -> Result<(), EventLoopClosed> {
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||||
self.events_loop_proxy.wakeup()
|
self.events_loop_proxy.send_event(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ pub trait EventLoopExt {
|
|||||||
fn new_dpi_unaware() -> Self where Self: Sized;
|
fn new_dpi_unaware() -> Self where Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventLoopExt for EventLoop {
|
impl<T> EventLoopExt for EventLoop<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn new_dpi_unaware() -> Self {
|
fn new_dpi_unaware() -> Self {
|
||||||
EventLoop {
|
EventLoop {
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ use std::os::windows::ffi::OsStringExt;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
use std::rc::Rc;
|
use crossbeam_channel::Sender;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
use winapi::ctypes::c_void;
|
use winapi::ctypes::c_void;
|
||||||
use winapi::shared::guiddef::REFIID;
|
use winapi::shared::guiddef::REFIID;
|
||||||
@@ -26,7 +24,7 @@ pub struct FileDropHandlerData {
|
|||||||
pub interface: IDropTarget,
|
pub interface: IDropTarget,
|
||||||
refcount: AtomicUsize,
|
refcount: AtomicUsize,
|
||||||
window: HWND,
|
window: HWND,
|
||||||
event_queue: Rc<RefCell<VecDeque<Event>>>,
|
event_sender: Sender<Event<()>>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FileDropHandler {
|
pub struct FileDropHandler {
|
||||||
@@ -35,14 +33,14 @@ pub struct FileDropHandler {
|
|||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
impl FileDropHandler {
|
impl FileDropHandler {
|
||||||
pub fn new(window: HWND, event_queue: Rc<RefCell<VecDeque<Event>>>) -> FileDropHandler {
|
pub fn new(window: HWND, event_sender: Sender<Event<()>>) -> FileDropHandler {
|
||||||
let data = Box::new(FileDropHandlerData {
|
let data = Box::new(FileDropHandlerData {
|
||||||
interface: IDropTarget {
|
interface: IDropTarget {
|
||||||
lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl,
|
lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl,
|
||||||
},
|
},
|
||||||
refcount: AtomicUsize::new(1),
|
refcount: AtomicUsize::new(1),
|
||||||
window,
|
window,
|
||||||
event_queue,
|
event_sender,
|
||||||
});
|
});
|
||||||
FileDropHandler {
|
FileDropHandler {
|
||||||
data: Box::into_raw(data),
|
data: Box::into_raw(data),
|
||||||
@@ -187,8 +185,8 @@ impl FileDropHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FileDropHandlerData {
|
impl FileDropHandlerData {
|
||||||
fn send_event(&self, event: Event) {
|
fn send_event(&self, event: Event<()>) {
|
||||||
self.event_queue.borrow_mut().push_back(event);
|
self.event_sender.send(event).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,11 @@
|
|||||||
|
|
||||||
use winapi::shared::basetsd::DWORD_PTR;
|
use winapi::shared::basetsd::DWORD_PTR;
|
||||||
use winapi::shared::basetsd::UINT_PTR;
|
use winapi::shared::basetsd::UINT_PTR;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use crossbeam_channel::{self, Sender, Receiver};
|
||||||
|
|
||||||
use winapi::ctypes::c_int;
|
use winapi::ctypes::c_int;
|
||||||
use winapi::shared::minwindef::{
|
use winapi::shared::minwindef::{
|
||||||
@@ -107,18 +105,6 @@ pub struct WindowState {
|
|||||||
pub mouse_buttons_down: u32
|
pub mouse_buttons_down: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct SubclassInput {
|
|
||||||
pub window_state: Arc<Mutex<WindowState>>,
|
|
||||||
pub event_queue: Rc<RefCell<VecDeque<Event>>>,
|
|
||||||
pub file_drop_handler: FileDropHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubclassInput {
|
|
||||||
fn send_event(&self, event: Event) {
|
|
||||||
self.event_queue.borrow_mut().push_back(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WindowState {
|
impl WindowState {
|
||||||
pub fn update_min_max(&mut self, old_dpi_factor: f64, new_dpi_factor: f64) {
|
pub fn update_min_max(&mut self, old_dpi_factor: f64, new_dpi_factor: f64) {
|
||||||
let scale_factor = new_dpi_factor / old_dpi_factor;
|
let scale_factor = new_dpi_factor / old_dpi_factor;
|
||||||
@@ -132,30 +118,55 @@ impl WindowState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EventLoop {
|
pub(crate) struct SubclassInput {
|
||||||
// Id of the background thread from the Win32 API.
|
pub window_state: Arc<Mutex<WindowState>>,
|
||||||
thread_id: DWORD,
|
pub event_send: Sender<Event<()>>,
|
||||||
pub(crate) event_queue: Rc<RefCell<VecDeque<Event>>>
|
pub file_drop_handler: FileDropHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventLoop {
|
impl SubclassInput {
|
||||||
pub fn new() -> EventLoop {
|
fn send_event(&self, event: Event<()>) {
|
||||||
|
self.event_send.send(event).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventLoop<T> {
|
||||||
|
// Id of the background thread from the Win32 API.
|
||||||
|
thread_id: DWORD,
|
||||||
|
|
||||||
|
pub(crate) event_send: Sender<Event<()>>,
|
||||||
|
pub(crate) user_event_send: Sender<T>,
|
||||||
|
|
||||||
|
event_recv: Receiver<Event<()>>,
|
||||||
|
user_event_recv: Receiver<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoop<T> {
|
||||||
|
pub fn new() -> EventLoop<T> {
|
||||||
Self::with_dpi_awareness(true)
|
Self::with_dpi_awareness(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_dpi_awareness(dpi_aware: bool) -> EventLoop {
|
pub fn with_dpi_awareness(dpi_aware: bool) -> EventLoop<T> {
|
||||||
become_dpi_aware(dpi_aware);
|
become_dpi_aware(dpi_aware);
|
||||||
|
|
||||||
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
||||||
|
|
||||||
|
let (event_send, event_recv) = crossbeam_channel::unbounded();
|
||||||
|
let (user_event_send, user_event_recv) = crossbeam_channel::unbounded();
|
||||||
|
|
||||||
EventLoop {
|
EventLoop {
|
||||||
thread_id,
|
thread_id,
|
||||||
event_queue: Rc::new(RefCell::new(VecDeque::new()))
|
|
||||||
|
event_send,
|
||||||
|
event_recv,
|
||||||
|
|
||||||
|
user_event_send,
|
||||||
|
user_event_recv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run<F>(self, mut event_handler: F) -> !
|
pub fn run<F>(self, mut event_handler: F) -> !
|
||||||
where F: 'static + FnMut(Event, &::EventLoop, &mut ControlFlow)
|
where F: 'static + FnMut(Event<T>, &::EventLoop<T>, &mut ControlFlow)
|
||||||
{
|
{
|
||||||
let event_loop = ::EventLoop {
|
let event_loop = ::EventLoop {
|
||||||
events_loop: self,
|
events_loop: self,
|
||||||
@@ -239,9 +250,8 @@ impl EventLoop {
|
|||||||
|
|
||||||
while has_message {
|
while has_message {
|
||||||
match msg.message {
|
match msg.message {
|
||||||
x if x == *WAKEUP_MSG_ID => {
|
// Handler is called in loop below.
|
||||||
call_event_handler!(Event::Awakened);
|
x if x == *WAKEUP_MSG_ID => (),
|
||||||
},
|
|
||||||
x if x == *EXEC_MSG_ID => {
|
x if x == *EXEC_MSG_ID => {
|
||||||
let mut function: Box<Box<FnMut()>> = Box::from_raw(msg.wParam as usize as *mut _);
|
let mut function: Box<Box<FnMut()>> = Box::from_raw(msg.wParam as usize as *mut _);
|
||||||
function()
|
function()
|
||||||
@@ -254,14 +264,16 @@ impl EventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// For whatever reason doing this in a `whlie let` loop doesn't drop the `RefMut`,
|
let full_event: Option<Event<T>> = select! {
|
||||||
// so we have to do it like this.
|
recv(event_loop.events_loop.event_recv) -> event =>
|
||||||
let event = match event_loop.events_loop.event_queue.borrow_mut().pop_front() {
|
event.ok().map(|e| e.map_nonuser_event().expect("User event sent through nonuser channel")),
|
||||||
Some(event) => event,
|
recv(event_loop.events_loop.user_event_recv) -> user_event =>
|
||||||
None => break
|
user_event.ok().map(|e| Event::UserEvent(e)),
|
||||||
|
default => break
|
||||||
};
|
};
|
||||||
|
if let Some(full_event) = full_event {
|
||||||
call_event_handler!(event);
|
call_event_handler!(full_event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) {
|
if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) {
|
||||||
@@ -281,9 +293,17 @@ impl EventLoop {
|
|||||||
::std::process::exit(0);
|
::std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_proxy(&self) -> EventLoopProxy {
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||||
EventLoopProxy {
|
EventLoopProxy {
|
||||||
thread_id: self.thread_id,
|
thread_id: self.thread_id,
|
||||||
|
event_send: self.user_event_send.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor {
|
||||||
|
EventLoopThreadExecutor {
|
||||||
|
thread_id: self.thread_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,7 +330,7 @@ pub fn dur2timeout(dur: Duration) -> DWORD {
|
|||||||
}).unwrap_or(winbase::INFINITE)
|
}).unwrap_or(winbase::INFINITE)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for EventLoop {
|
impl<T> Drop for EventLoop<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Posting `WM_QUIT` will cause `GetMessage` to stop.
|
// Posting `WM_QUIT` will cause `GetMessage` to stop.
|
||||||
@@ -319,29 +339,11 @@ impl Drop for EventLoop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub(crate) struct EventLoopThreadExecutor {
|
||||||
pub struct EventLoopProxy {
|
thread_id: DWORD
|
||||||
thread_id: DWORD,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventLoopProxy {
|
impl EventLoopThreadExecutor {
|
||||||
pub fn wakeup(&self) -> Result<(), EventLoopClosed> {
|
|
||||||
unsafe {
|
|
||||||
if winuser::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(EventLoopClosed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check to see if we're in the parent event loop's thread.
|
/// Check to see if we're in the parent event loop's thread.
|
||||||
pub(super) fn in_event_loop_thread(&self) -> bool {
|
pub(super) fn in_event_loop_thread(&self) -> bool {
|
||||||
let cur_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
let cur_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() };
|
||||||
@@ -384,6 +386,32 @@ impl EventLoopProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EventLoopProxy<T> {
|
||||||
|
thread_id: DWORD,
|
||||||
|
event_send: Sender<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoopProxy<T> {
|
||||||
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||||
|
unsafe {
|
||||||
|
if winuser::PostThreadMessageA(self.thread_id, *WAKEUP_MSG_ID, 0, 0) != 0 {
|
||||||
|
self.event_send.send(event).ok();
|
||||||
|
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(EventLoopClosed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// Message sent by the `EventLoopProxy` when we want to wake up the thread.
|
// Message sent by the `EventLoopProxy` when we want to wake up the thread.
|
||||||
// WPARAM and LPARAM are unused.
|
// WPARAM and LPARAM are unused.
|
||||||
|
|||||||
@@ -71,19 +71,19 @@ pub fn get_primary_monitor() -> MonitorId {
|
|||||||
MonitorId::from_hmonitor(hmonitor)
|
MonitorId::from_hmonitor(hmonitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventLoop {
|
pub fn get_current_monitor(hwnd: HWND) -> MonitorId {
|
||||||
|
let hmonitor = unsafe {
|
||||||
|
winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST)
|
||||||
|
};
|
||||||
|
MonitorId::from_hmonitor(hmonitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> EventLoop<T> {
|
||||||
// TODO: Investigate opportunities for caching
|
// TODO: Investigate opportunities for caching
|
||||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||||
get_available_monitors()
|
get_available_monitors()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_monitor(hwnd: HWND) -> MonitorId {
|
|
||||||
let hmonitor = unsafe {
|
|
||||||
winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST)
|
|
||||||
};
|
|
||||||
MonitorId::from_hmonitor(hmonitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||||
get_primary_monitor()
|
get_primary_monitor()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use platform::platform::{Cursor, PlatformSpecificWindowBuilderAttributes, Window
|
|||||||
use platform::platform::dpi::{dpi_to_scale_factor, get_hwnd_dpi};
|
use platform::platform::dpi::{dpi_to_scale_factor, get_hwnd_dpi};
|
||||||
use platform::platform::events_loop::{self, DESTROY_MSG_ID, EventLoop, INITIAL_DPI_MSG_ID, WindowState};
|
use platform::platform::events_loop::{self, DESTROY_MSG_ID, EventLoop, INITIAL_DPI_MSG_ID, WindowState};
|
||||||
use platform::platform::icon::{self, IconType, WinIcon};
|
use platform::platform::icon::{self, IconType, WinIcon};
|
||||||
use platform::platform::monitor::get_available_monitors;
|
use platform::platform::monitor;
|
||||||
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
||||||
use platform::platform::drop_handler::FileDropHandler;
|
use platform::platform::drop_handler::FileDropHandler;
|
||||||
use platform::platform::util;
|
use platform::platform::util;
|
||||||
@@ -48,7 +48,7 @@ pub struct Window {
|
|||||||
window_state: Arc<Mutex<WindowState>>,
|
window_state: Arc<Mutex<WindowState>>,
|
||||||
|
|
||||||
// The events loop proxy.
|
// The events loop proxy.
|
||||||
events_loop_proxy: events_loop::EventLoopProxy,
|
thread_executor: events_loop::EventLoopThreadExecutor,
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
|
// https://blogs.msdn.microsoft.com/oldnewthing/20131017-00/?p=2903
|
||||||
@@ -74,8 +74,8 @@ unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> B
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new(
|
pub fn new<T>(
|
||||||
events_loop: &EventLoop,
|
events_loop: &EventLoop<T>,
|
||||||
w_attr: WindowAttributes,
|
w_attr: WindowAttributes,
|
||||||
pl_attr: PlatformSpecificWindowBuilderAttributes,
|
pl_attr: PlatformSpecificWindowBuilderAttributes,
|
||||||
) -> Result<Window, CreationError> {
|
) -> Result<Window, CreationError> {
|
||||||
@@ -322,7 +322,7 @@ impl Window {
|
|||||||
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
_ => winuser::IDC_ARROW, // use arrow for the missing cases.
|
||||||
});
|
});
|
||||||
self.window_state.lock().cursor = cursor_id;
|
self.window_state.lock().cursor = cursor_id;
|
||||||
self.events_loop_proxy.execute_in_thread(move || unsafe {
|
self.thread_executor.execute_in_thread(move || unsafe {
|
||||||
let cursor = winuser::LoadCursorW(
|
let cursor = winuser::LoadCursorW(
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
cursor_id.0,
|
cursor_id.0,
|
||||||
@@ -384,7 +384,7 @@ impl Window {
|
|||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
let window_state = Arc::clone(&self.window_state);
|
let window_state = Arc::clone(&self.window_state);
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
self.events_loop_proxy.execute_in_thread(move || {
|
self.thread_executor.execute_in_thread(move || {
|
||||||
let result = unsafe { Self::grab_cursor_inner(&window, grab) };
|
let result = unsafe { Self::grab_cursor_inner(&window, grab) };
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
window_state.lock().cursor_grabbed = grab;
|
window_state.lock().cursor_grabbed = grab;
|
||||||
@@ -410,7 +410,7 @@ impl Window {
|
|||||||
if hide == window_state_lock.cursor_hidden { return; }
|
if hide == window_state_lock.cursor_hidden { return; }
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
let window_state = Arc::clone(&self.window_state);
|
let window_state = Arc::clone(&self.window_state);
|
||||||
self.events_loop_proxy.execute_in_thread(move || {
|
self.thread_executor.execute_in_thread(move || {
|
||||||
unsafe { Self::hide_cursor_inner(hide) };
|
unsafe { Self::hide_cursor_inner(hide) };
|
||||||
window_state.lock().cursor_hidden = hide;
|
window_state.lock().cursor_hidden = hide;
|
||||||
let _ = tx.send(());
|
let _ = tx.send(());
|
||||||
@@ -458,7 +458,7 @@ impl Window {
|
|||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
unsafe {
|
unsafe {
|
||||||
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||||
self.events_loop_proxy.execute_in_thread(move || {
|
self.thread_executor.execute_in_thread(move || {
|
||||||
winuser::ShowWindow(
|
winuser::ShowWindow(
|
||||||
window.0,
|
window.0,
|
||||||
if maximized {
|
if maximized {
|
||||||
@@ -524,7 +524,7 @@ impl Window {
|
|||||||
drop(window_state_lock);
|
drop(window_state_lock);
|
||||||
// We're restoring the window to its size and position from before being fullscreened.
|
// We're restoring the window to its size and position from before being fullscreened.
|
||||||
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
// `ShowWindow` resizes the window, so it must be called from the main thread.
|
||||||
self.events_loop_proxy.execute_in_thread(move || {
|
self.thread_executor.execute_in_thread(move || {
|
||||||
let _ = Self::grab_cursor_inner(&window, false);
|
let _ = Self::grab_cursor_inner(&window, false);
|
||||||
|
|
||||||
if resizable {
|
if resizable {
|
||||||
@@ -585,7 +585,7 @@ impl Window {
|
|||||||
window_state_lock.fullscreen = monitor;
|
window_state_lock.fullscreen = monitor;
|
||||||
drop(window_state_lock);
|
drop(window_state_lock);
|
||||||
|
|
||||||
self.events_loop_proxy.execute_in_thread(move || {
|
self.thread_executor.execute_in_thread(move || {
|
||||||
let _ = Self::grab_cursor_inner(&window, false);
|
let _ = Self::grab_cursor_inner(&window, false);
|
||||||
|
|
||||||
winuser::SetWindowLongW(
|
winuser::SetWindowLongW(
|
||||||
@@ -681,7 +681,7 @@ impl Window {
|
|||||||
|
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
|
|
||||||
self.events_loop_proxy.execute_in_thread(move || {
|
self.thread_executor.execute_in_thread(move || {
|
||||||
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
||||||
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
||||||
winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _);
|
winuser::AdjustWindowRectEx(&mut rect, style as _, 0, ex_style as _);
|
||||||
@@ -709,7 +709,7 @@ impl Window {
|
|||||||
let mut window_state = self.window_state.lock();
|
let mut window_state = self.window_state.lock();
|
||||||
if mem::replace(&mut window_state.always_on_top, always_on_top) != always_on_top {
|
if mem::replace(&mut window_state.always_on_top, always_on_top) != always_on_top {
|
||||||
let window = self.window.clone();
|
let window = self.window.clone();
|
||||||
self.events_loop_proxy.execute_in_thread(move || {
|
self.thread_executor.execute_in_thread(move || {
|
||||||
let insert_after = if always_on_top {
|
let insert_after = if always_on_top {
|
||||||
winuser::HWND_TOPMOST
|
winuser::HWND_TOPMOST
|
||||||
} else {
|
} else {
|
||||||
@@ -734,7 +734,7 @@ impl Window {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||||
RootMonitorId {
|
RootMonitorId {
|
||||||
inner: EventLoop::get_current_monitor(self.window.0),
|
inner: monitor::get_current_monitor(self.window.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -809,10 +809,10 @@ pub unsafe fn adjust_size(
|
|||||||
(rect.right - rect.left, rect.bottom - rect.top)
|
(rect.right - rect.left, rect.bottom - rect.top)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn init(
|
unsafe fn init<T>(
|
||||||
mut attributes: WindowAttributes,
|
mut attributes: WindowAttributes,
|
||||||
mut pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
mut pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||||
event_loop: &events_loop::EventLoop,
|
event_loop: &events_loop::EventLoop<T>,
|
||||||
) -> Result<Window, CreationError> {
|
) -> Result<Window, CreationError> {
|
||||||
let title = OsStr::new(&attributes.title)
|
let title = OsStr::new(&attributes.title)
|
||||||
.encode_wide()
|
.encode_wide()
|
||||||
@@ -848,7 +848,7 @@ unsafe fn init(
|
|||||||
let class_name = register_window_class(&window_icon, &taskbar_icon);
|
let class_name = register_window_class(&window_icon, &taskbar_icon);
|
||||||
|
|
||||||
let guessed_dpi_factor = {
|
let guessed_dpi_factor = {
|
||||||
let monitors = get_available_monitors();
|
let monitors = monitor::get_available_monitors();
|
||||||
let dpi_factor = if !monitors.is_empty() {
|
let dpi_factor = if !monitors.is_empty() {
|
||||||
let mut dpi_factor = Some(monitors[0].get_hidpi_factor());
|
let mut dpi_factor = Some(monitors[0].get_hidpi_factor());
|
||||||
for monitor in &monitors {
|
for monitor in &monitors {
|
||||||
@@ -1055,7 +1055,7 @@ unsafe fn init(
|
|||||||
let win = Window {
|
let win = Window {
|
||||||
window: real_window,
|
window: real_window,
|
||||||
window_state,
|
window_state,
|
||||||
events_loop_proxy: event_loop.create_proxy(),
|
thread_executor: event_loop.create_thread_executor(),
|
||||||
};
|
};
|
||||||
|
|
||||||
win.set_maximized(attributes.maximized);
|
win.set_maximized(attributes.maximized);
|
||||||
@@ -1076,7 +1076,7 @@ unsafe fn init(
|
|||||||
panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`");
|
panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`");
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_drop_handler = FileDropHandler::new(win.window.0, event_loop.event_queue.clone());
|
let file_drop_handler = FileDropHandler::new(win.window.0, event_loop.event_send.clone());
|
||||||
let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET;
|
let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET;
|
||||||
|
|
||||||
assert_eq!(ole2::RegisterDragDrop(win.window.0, handler_interface_ptr), S_OK);
|
assert_eq!(ole2::RegisterDragDrop(win.window.0, handler_interface_ptr), S_OK);
|
||||||
@@ -1085,8 +1085,8 @@ unsafe fn init(
|
|||||||
|
|
||||||
let subclass_input = events_loop::SubclassInput {
|
let subclass_input = events_loop::SubclassInput {
|
||||||
window_state: win.window_state.clone(),
|
window_state: win.window_state.clone(),
|
||||||
event_queue: event_loop.event_queue.clone(),
|
event_send: event_loop.event_send.clone(),
|
||||||
file_drop_handler
|
file_drop_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
events_loop::subclass_window(win.window.0, subclass_input);
|
events_loop::subclass_window(win.window.0, subclass_input);
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ impl WindowBuilder {
|
|||||||
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
||||||
/// out of memory, etc.
|
/// out of memory, etc.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn build(mut self, events_loop: &EventLoop) -> Result<Window, CreationError> {
|
pub fn build<T>(mut self, events_loop: &EventLoop<T>) -> Result<Window, CreationError> {
|
||||||
self.window.dimensions = Some(self.window.dimensions.unwrap_or_else(|| {
|
self.window.dimensions = Some(self.window.dimensions.unwrap_or_else(|| {
|
||||||
if let Some(ref monitor) = self.window.fullscreen {
|
if let Some(ref monitor) = self.window.fullscreen {
|
||||||
// resizing the window to the dimensions of the monitor when fullscreen
|
// resizing the window to the dimensions of the monitor when fullscreen
|
||||||
@@ -170,7 +170,7 @@ impl Window {
|
|||||||
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
||||||
/// out of memory, etc.
|
/// out of memory, etc.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(events_loop: &EventLoop) -> Result<Window, CreationError> {
|
pub fn new<T>(events_loop: &EventLoop<T>) -> Result<Window, CreationError> {
|
||||||
let builder = WindowBuilder::new();
|
let builder = WindowBuilder::new();
|
||||||
builder.build(events_loop)
|
builder.build(events_loop)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ fn needs_send<T:Send>() {}
|
|||||||
#[test]
|
#[test]
|
||||||
fn events_loop_proxy_send() {
|
fn events_loop_proxy_send() {
|
||||||
// ensures that `winit::EventLoopProxy` implements `Send`
|
// ensures that `winit::EventLoopProxy` implements `Send`
|
||||||
needs_send::<winit::EventLoopProxy>();
|
needs_send::<winit::EventLoopProxy<()>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user