diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ace1cb69fc..92328839e23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,41 @@ # Unreleased - -# Version 0.19.1 (2019-04-08) - -- On Wayland, added a `get_wayland_display` function to `EventsLoopExt`. -- On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus. -- On macOS, fix command key event left and right reverse. -- On FreeBSD, NetBSD, and OpenBSD, fix build of X11 backend. -- On Windows, fix icon not showing up in corner of window. -- On X11, change DPI scaling factor behavior. First, winit tries to read it from "Xft.dpi" XResource, and uses DPI calculation from xrandr dimensions as fallback behavior. - -# Version 0.19.0 (2019-03-06) - -- On X11, we will use the faster `XRRGetScreenResourcesCurrent` function instead of `XRRGetScreenResources` when available. -- On macOS, fix keycodes being incorrect when using a non-US keyboard layout. -- On Wayland, fix `with_title()` not setting the windows title +- Changes below are considered **breaking**. +- Change all occurrences of `EventsLoop` to `EventLoop`. +- Previously flat API is now exposed through `event`, `event_loop`, `monitor`, and `window` modules. +- `os` module changes: + - Renamed to `platform`. + - All traits now have platform-specific suffixes. + - Exposes new `desktop` module on Windows, Mac, and Linux. +- Changes to event loop types: + - `EventLoopProxy::wakeup` has been removed in favor of `send_event`. + - **Major:** New `run` method drives winit event loop. + - Returns `!` to ensure API behaves identically across all supported platforms. + - This allows `emscripten` implementation to work without lying about the API. + - `ControlFlow`'s variants have been replaced with `Wait`, `WaitUntil(Instant)`, `Poll`, and `Exit`. + - Is read after `EventsCleared` is processed. + - `Wait` waits until new events are available. + - `WaitUntil` waits until either new events are available or the provided time has been reached. + - `Poll` instantly resumes the event loop. + - `Exit` aborts the event loop. + - Takes a closure that implements `'static + FnMut(Event, &EventLoop, &mut ControlFlow)`. + - `&EventLoop` is provided to allow new `Window`s to be created. + - **Major:** `platform::desktop` module exposes `EventLoopExtDesktop` trait with `run_return` method. + - Behaves identically to `run`, but returns control flow to the calling context and can take non-`'static` closures. + - `EventLoop`'s `poll_events` and `run_forever` methods have been removed in favor of `run` and `run_return`. +- Changes to events: + - Remove `Event::Awakened` in favor of `Event::UserEvent(T)`. + - Can be sent with `EventLoopProxy::send_event`. + - Rename `WindowEvent::Refresh` to `WindowEvent::RedrawRequested`. + - `RedrawRequested` can be sent by the user with the `Window::request_redraw` method. + - `EventLoop`, `EventLoopProxy`, and `Event` are now generic over `T`, for use in `UserEvent`. + - **Major:** Add `NewEvents(StartCause)`, `EventsCleared`, and `LoopDestroyed` variants to `Event`. + - `NewEvents` is emitted when new events are ready to be processed by event loop. + - `StartCause` describes why new events are available, with `ResumeTimeReached`, `Poll`, `WaitCancelled`, and `Init` (sent once at start of loop). + - `EventsCleared` is emitted when all available events have been processed. + - Can be used to perform logic that depends on all events being processed (e.g. an iteration of a game loop). + - `LoopDestroyed` is emitted when the `run` or `run_return` method is about to exit. +- Rename `MonitorId` to `MonitorHandle`. +- Removed `serde` implementations from `ControlFlow`. - On Wayland, add `set_wayland_theme()` to control client decoration color theme - Added serde serialization to `os::unix::XWindowType`. - **Breaking:** `image` crate upgraded to 0.21. This is exposed as part of the `icon_loading` API. diff --git a/Cargo.toml b/Cargo.toml index 2efc1adde3e..95ef2991ff9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ bitflags = "1" version = "0.3.6" features = [ "combaseapi", + "commctrl", "dwmapi", "errhandlingapi", "hidusage", @@ -70,3 +71,6 @@ parking_lot = "0.7" percent-encoding = "1.0" gbm = { git = "https://github.com/kosyak/gbm.rs", default-features = false, features = ["drm-support", "import-egl", "gen"] } drm = { git = "https://github.com/kosyak/drm-rs", branch = "master" } + +[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "windows"))'.dependencies.parking_lot] +version = "0.7" diff --git a/README.md b/README.md index b79568af574..1daa6b9b276 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,10 @@ another library. extern crate winit; fn main() { - let mut events_loop = winit::EventsLoop::new(); - let window = winit::Window::new(&events_loop).unwrap(); + let mut event_loop = winit::EventLoop::new(); + let window = winit::Window::new(&event_loop).unwrap(); - events_loop.run_forever(|event| { + event_loop.run(|event| { match event { winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, diff --git a/examples/cursor.rs b/examples/cursor.rs index d2df71a7aff..27c003ac2d2 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -1,32 +1,48 @@ extern crate winit; -use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow}; +use winit::window::{WindowBuilder, MouseCursor}; +use winit::event::{Event, WindowEvent, ElementState, KeyboardInput}; +use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); - let window = winit::WindowBuilder::new().build(&events_loop).unwrap(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); window.set_title("A fantastic window!"); - let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize]; let mut cursor_idx = 0; - events_loop.run_forever(|event| { + event_loop.run(move |event, _, control_flow| { match event { Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => { - println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]); - window.set_cursor(cursors[cursor_idx]); - if cursor_idx < cursors.len() - 1 { + println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]); + window.set_cursor(CURSORS[cursor_idx]); + if cursor_idx < CURSORS.len() - 1 { cursor_idx += 1; } else { cursor_idx = 0; } }, Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { - return ControlFlow::Break; + *control_flow = ControlFlow::Exit; + return; }, _ => () } - ControlFlow::Continue }); } + +const CURSORS: &[MouseCursor] = &[ + MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, + MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, + MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, + MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::Cell, + MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, + MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, + MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, + MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, + MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, + MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, + MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, + MouseCursor::ColResize, MouseCursor::RowResize +]; diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 641a324ded4..741d0d74aa4 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,30 +1,34 @@ extern crate winit; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent, ElementState, KeyboardInput}; +use winit::event_loop::{EventLoop, ControlFlow}; + fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); - let window = winit::WindowBuilder::new() + let window = WindowBuilder::new() .with_title("Super Cursor Grab'n'Hide Simulator 9000") - .build(&events_loop) + .build(&event_loop) .unwrap(); - events_loop.run_forever(|event| { - if let winit::Event::WindowEvent { event, .. } = event { - use winit::WindowEvent::*; + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + if let Event::WindowEvent { event, .. } = event { match event { - CloseRequested => return winit::ControlFlow::Break, - KeyboardInput { - input: winit::KeyboardInput { - state: winit::ElementState::Released, + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { + input: KeyboardInput { + state: ElementState::Released, virtual_keycode: Some(key), modifiers, .. }, .. } => { - use winit::VirtualKeyCode::*; + use winit::event::VirtualKeyCode::*; match key { - Escape => return winit::ControlFlow::Break, + Escape => *control_flow = ControlFlow::Exit, G => window.grab_cursor(!modifiers.shift).unwrap(), H => window.hide_cursor(!modifiers.shift), _ => (), @@ -33,6 +37,5 @@ fn main() { _ => (), } } - winit::ControlFlow::Continue }); } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index c29013c41ac..788a608c853 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -1,10 +1,13 @@ extern crate winit; use std::io::{self, Write}; -use winit::{ControlFlow, Event, WindowEvent}; +use winit::monitor::MonitorHandle; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent, VirtualKeyCode, ElementState, KeyboardInput}; +use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); #[cfg(target_os = "macos")] let mut macos_use_simple_fullscreen = false; @@ -26,43 +29,44 @@ fn main() { // Prompt for monitor when using native fullscreen if !macos_use_simple_fullscreen { - Some(prompt_for_monitor(&events_loop)) + Some(prompt_for_monitor(&event_loop)) } else { None } } #[cfg(not(target_os = "macos"))] - Some(prompt_for_monitor(&events_loop)) + Some(prompt_for_monitor(&event_loop)) }; let mut is_fullscreen = monitor.is_some(); let mut is_maximized = false; let mut decorations = true; - let window = winit::WindowBuilder::new() + let window = WindowBuilder::new() .with_title("Hello world!") .with_fullscreen(monitor) - .build(&events_loop) + .build(&event_loop) .unwrap(); - events_loop.run_forever(|event| { + event_loop.run(move |event, _, control_flow| { println!("{:?}", event); + *control_flow = ControlFlow::Wait; match event { Event::WindowEvent { event, .. } => match event { - WindowEvent::CloseRequested => return ControlFlow::Break, + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::KeyboardInput { input: - winit::KeyboardInput { + KeyboardInput { virtual_keycode: Some(virtual_code), state, .. }, .. } => match (virtual_code, state) { - (winit::VirtualKeyCode::Escape, _) => return ControlFlow::Break, - (winit::VirtualKeyCode::F, winit::ElementState::Pressed) => { + (VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit, + (VirtualKeyCode::F, ElementState::Pressed) => { #[cfg(target_os = "macos")] { if macos_use_simple_fullscreen { @@ -82,11 +86,11 @@ fn main() { window.set_fullscreen(Some(window.get_current_monitor())); } } - (winit::VirtualKeyCode::M, winit::ElementState::Pressed) => { + (VirtualKeyCode::M, ElementState::Pressed) => { is_maximized = !is_maximized; window.set_maximized(is_maximized); } - (winit::VirtualKeyCode::D, winit::ElementState::Pressed) => { + (VirtualKeyCode::D, ElementState::Pressed) => { decorations = !decorations; window.set_decorations(decorations); } @@ -96,14 +100,12 @@ fn main() { }, _ => {} } - - ControlFlow::Continue }); } // Enumerate monitors and prompt user to choose one -fn prompt_for_monitor(events_loop: &winit::EventsLoop) -> winit::MonitorId { - for (num, monitor) in events_loop.get_available_monitors().enumerate() { +fn prompt_for_monitor(event_loop: &EventLoop<()>) -> MonitorHandle { + for (num, monitor) in event_loop.get_available_monitors().enumerate() { println!("Monitor #{}: {:?}", num, monitor.get_name()); } @@ -113,7 +115,7 @@ fn prompt_for_monitor(events_loop: &winit::EventsLoop) -> winit::MonitorId { let mut num = String::new(); io::stdin().read_line(&mut num).unwrap(); let num = num.trim().parse().ok().expect("Please enter a number"); - let monitor = events_loop.get_available_monitors().nth(num).expect("Please enter a valid ID"); + let monitor = event_loop.get_available_monitors().nth(num).expect("Please enter a valid ID"); println!("Using {:?}", monitor.get_name()); diff --git a/examples/handling_close.rs b/examples/handling_close.rs index 101f45cf32b..31f17da33e6 100644 --- a/examples/handling_close.rs +++ b/examples/handling_close.rs @@ -1,23 +1,27 @@ extern crate winit; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent, KeyboardInput}; +use winit::event_loop::{EventLoop, ControlFlow}; + fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); - let _window = winit::WindowBuilder::new() + let _window = WindowBuilder::new() .with_title("Your faithful window") - .build(&events_loop) + .build(&event_loop) .unwrap(); let mut close_requested = false; - events_loop.run_forever(|event| { - use winit::WindowEvent::*; - use winit::ElementState::Released; - use winit::VirtualKeyCode::{N, Y}; + event_loop.run(move |event, _, control_flow| { + use winit::event::ElementState::Released; + use winit::event::VirtualKeyCode::{N, Y}; + *control_flow = ControlFlow::Wait; match event { - winit::Event::WindowEvent { event, .. } => match event { - CloseRequested => { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => { // `CloseRequested` is sent when the close button on the window is pressed (or // through whatever other mechanisms the window manager provides for closing a // window). If you don't handle this event, the close button won't actually do @@ -34,9 +38,9 @@ fn main() { // closing the window. How to close the window is detailed in the handler for // the Y key. } - KeyboardInput { + WindowEvent::KeyboardInput { input: - winit::KeyboardInput { + KeyboardInput { virtual_keycode: Some(virtual_code), state: Released, .. @@ -53,7 +57,7 @@ fn main() { // event loop (i.e. if it's a multi-window application), you need to // drop the window. That closes it, and results in `Destroyed` being // sent. - return winit::ControlFlow::Break; + *control_flow = ControlFlow::Exit; } } N => { @@ -68,7 +72,5 @@ fn main() { }, _ => (), } - - winit::ControlFlow::Continue }); } diff --git a/examples/min_max_size.rs b/examples/min_max_size.rs index 06f4aa18d2c..dea437635c5 100644 --- a/examples/min_max_size.rs +++ b/examples/min_max_size.rs @@ -1,23 +1,27 @@ extern crate winit; use winit::dpi::LogicalSize; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); - let window = winit::WindowBuilder::new() - .build(&events_loop) + let window = WindowBuilder::new() + .build(&event_loop) .unwrap(); window.set_min_dimensions(Some(LogicalSize::new(400.0, 200.0))); window.set_max_dimensions(Some(LogicalSize::new(800.0, 400.0))); - events_loop.run_forever(|event| { + event_loop.run(move |event, _, control_flow| { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::ControlFlow::Break, - _ => winit::ControlFlow::Continue, + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => + *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, } }); } diff --git a/examples/monitor_list.rs b/examples/monitor_list.rs index e40ac309180..335aa02281b 100644 --- a/examples/monitor_list.rs +++ b/examples/monitor_list.rs @@ -1,7 +1,9 @@ extern crate winit; +use winit::event_loop::EventLoop; +use winit::window::WindowBuilder; fn main() { - let event_loop = winit::EventsLoop::new(); - let window = winit::WindowBuilder::new().build(&event_loop).unwrap(); + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); println!("{:#?}\nPrimary: {:#?}", window.get_available_monitors(), window.get_primary_monitor()); } diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index b558aa231a6..cf2703c2dd0 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -1,33 +1,42 @@ extern crate winit; use std::collections::HashMap; +use winit::window::Window; +use winit::event::{Event, WindowEvent, ElementState, KeyboardInput}; +use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); let mut windows = HashMap::new(); for _ in 0..3 { - let window = winit::Window::new(&events_loop).unwrap(); + let window = Window::new(&event_loop).unwrap(); windows.insert(window.id(), window); } - events_loop.run_forever(|event| { + event_loop.run(move |event, event_loop, control_flow| { + *control_flow = ControlFlow::Wait; match event { - winit::Event::WindowEvent { - event: winit::WindowEvent::CloseRequested, - window_id, - } => { - println!("Window {:?} has received the signal to close", window_id); + Event::WindowEvent { event, window_id } => { + match event { + WindowEvent::CloseRequested => { + println!("Window {:?} has received the signal to close", window_id); - // This drops the window, causing it to close. - windows.remove(&window_id); + // This drops the window, causing it to close. + windows.remove(&window_id); - if windows.is_empty() { - return winit::ControlFlow::Break; + if windows.is_empty() { + *control_flow = ControlFlow::Exit; + } + }, + WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => { + let window = Window::new(&event_loop).unwrap(); + windows.insert(window.id(), window); + }, + _ => () } } _ => (), } - winit::ControlFlow::Continue }) } diff --git a/examples/proxy.rs b/examples/proxy.rs index a9751815034..3e9b2e9dfa3 100644 --- a/examples/proxy.rs +++ b/examples/proxy.rs @@ -1,29 +1,34 @@ extern crate winit; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop: EventLoop = EventLoop::new_user_event(); - let _window = winit::WindowBuilder::new() + let _window = WindowBuilder::new() .with_title("A fantastic window!") - .build(&events_loop) + .build(&event_loop) .unwrap(); - let proxy = events_loop.create_proxy(); + let proxy = event_loop.create_proxy(); std::thread::spawn(move || { - // Wake up the `events_loop` once every second. + let mut counter = 0; + // Wake up the `event_loop` once every second. loop { std::thread::sleep(std::time::Duration::from_secs(1)); - proxy.wakeup().unwrap(); + proxy.send_event(counter).unwrap(); + counter += 1; } }); - events_loop.run_forever(|event| { + event_loop.run(move |event, _, control_flow| { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => - winit::ControlFlow::Break, - _ => winit::ControlFlow::Continue, + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => + *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, } }); } diff --git a/examples/request_redraw.rs b/examples/request_redraw.rs new file mode 100644 index 00000000000..4ed4e8fbaec --- /dev/null +++ b/examples/request_redraw.rs @@ -0,0 +1,31 @@ +extern crate winit; +use std::time::{Instant, Duration}; + +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow}; + +fn main() { + let event_loop = EventLoop::new(); + + let window = WindowBuilder::new() + .with_title("A fantastic window!") + .build(&event_loop) + .unwrap(); + + event_loop.run(move |event, _, control_flow| { + println!("{:?}", event); + + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => *control_flow = ControlFlow::Exit, + Event::EventsCleared => { + window.request_redraw(); + *control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0)) + }, + _ => () + } + }); +} diff --git a/examples/resizable.rs b/examples/resizable.rs index 749e8521215..f5690b86b70 100644 --- a/examples/resizable.rs +++ b/examples/resizable.rs @@ -1,26 +1,30 @@ extern crate winit; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent, VirtualKeyCode, ElementState, KeyboardInput}; +use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); let mut resizable = false; - let window = winit::WindowBuilder::new() + let window = WindowBuilder::new() .with_title("Hit space to toggle resizability.") .with_dimensions((400, 200).into()) .with_resizable(resizable) - .build(&events_loop) + .build(&event_loop) .unwrap(); - events_loop.run_forever(|event| { + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; match event { - winit::Event::WindowEvent { event, .. } => match event { - winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break, - winit::WindowEvent::KeyboardInput { + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, + WindowEvent::KeyboardInput { input: - winit::KeyboardInput { - virtual_keycode: Some(winit::VirtualKeyCode::Space), - state: winit::ElementState::Released, + KeyboardInput { + virtual_keycode: Some(VirtualKeyCode::Space), + state: ElementState::Released, .. }, .. @@ -33,6 +37,5 @@ fn main() { }, _ => (), }; - winit::ControlFlow::Continue }); } diff --git a/examples/timer.rs b/examples/timer.rs new file mode 100644 index 00000000000..3692d3277ef --- /dev/null +++ b/examples/timer.rs @@ -0,0 +1,32 @@ +extern crate winit; +use std::time::{Duration, Instant}; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent, StartCause}; +use winit::event_loop::{EventLoop, ControlFlow}; + +fn main() { + let event_loop = EventLoop::new(); + + let _window = WindowBuilder::new() + .with_title("A fantastic window!") + .build(&event_loop) + .unwrap(); + + event_loop.run(move |event, _, control_flow| { + println!("{:?}", event); + + match event { + Event::NewEvents(StartCause::Init) => + *control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0)), + Event::NewEvents(StartCause::ResumeTimeReached{..}) => { + *control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::new(1, 0)); + println!("\nTimer\n"); + }, + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => *control_flow = ControlFlow::Exit, + _ => () + } + }); +} diff --git a/examples/transparent.rs b/examples/transparent.rs index a558350f3e6..4da6f9f2dd9 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -1,20 +1,24 @@ extern crate winit; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); - let window = winit::WindowBuilder::new().with_decorations(false) + let window = WindowBuilder::new().with_decorations(false) .with_transparency(true) - .build(&events_loop).unwrap(); + .build(&event_loop).unwrap(); window.set_title("A fantastic window!"); - events_loop.run_forever(|event| { + event_loop.run(move |event, _, control_flow| { println!("{:?}", event); match event { - winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::ControlFlow::Break, - _ => winit::ControlFlow::Continue, + Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => + *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, } }); } diff --git a/examples/window.rs b/examples/window.rs index fcc7d9ea334..3d5c7cf7db7 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,22 +1,25 @@ extern crate winit; +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow}; fn main() { - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); - let _window = winit::WindowBuilder::new() + let _window = WindowBuilder::new() .with_title("A fantastic window!") - .build(&events_loop) + .build(&event_loop) .unwrap(); - events_loop.run_forever(|event| { + event_loop.run(|event, _, control_flow| { println!("{:?}", event); match event { - winit::Event::WindowEvent { - event: winit::WindowEvent::CloseRequested, + Event::WindowEvent { + event: WindowEvent::CloseRequested, .. - } => winit::ControlFlow::Break, - _ => winit::ControlFlow::Continue, + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, } }); } diff --git a/examples/window_icon.rs b/examples/window_icon.rs index 5be1433e01d..6991e81f000 100644 --- a/examples/window_icon.rs +++ b/examples/window_icon.rs @@ -8,7 +8,10 @@ extern crate image; #[cfg(feature = "icon_loading")] fn main() { - use winit::Icon; + use winit::window::{WindowBuilder, Icon}; + use winit::event::Event; + use winit::event_loop::{EventLoop, ControlFlow}; + // You'll have to choose an icon size at your own discretion. On X11, the desired size varies // by WM, and on Windows, you still have to account for screen scaling. Here we use 32px, // since it seems to work well enough in most cases. Be careful about going too high, or @@ -20,21 +23,22 @@ fn main() { // feature enabled). let icon = Icon::from_path(path).expect("Failed to open icon"); - let mut events_loop = winit::EventsLoop::new(); + let event_loop = EventLoop::new(); - let window = winit::WindowBuilder::new() + let window = WindowBuilder::new() .with_title("An iconic window!") // At present, this only does anything on Windows and X11, so if you want to save load // time, you can put icon loading behind a function that returns `None` on other platforms. .with_window_icon(Some(icon)) - .build(&events_loop) + .build(&event_loop) .unwrap(); - events_loop.run_forever(|event| { - if let winit::Event::WindowEvent { event, .. } = event { - use winit::WindowEvent::*; + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + if let Event::WindowEvent { event, .. } = event { + use winit::event::WindowEvent::*; match event { - CloseRequested => return winit::ControlFlow::Break, + CloseRequested => *control_flow = ControlFlow::Exit, DroppedFile(path) => { use image::GenericImageView; @@ -81,7 +85,6 @@ fn main() { _ => (), } } - winit::ControlFlow::Continue }); } diff --git a/examples/window_run_return.rs b/examples/window_run_return.rs new file mode 100644 index 00000000000..ff619902e62 --- /dev/null +++ b/examples/window_run_return.rs @@ -0,0 +1,45 @@ +extern crate winit; + +use winit::window::WindowBuilder; +use winit::event::{Event, WindowEvent}; +use winit::event_loop::{EventLoop, ControlFlow}; +use winit::platform::desktop::EventLoopExtDesktop; + +fn main() { + let mut event_loop = EventLoop::new(); + + let window = WindowBuilder::new() + .with_title("A fantastic window!") + .build(&event_loop) + .unwrap(); + + println!("Close the window to continue."); + event_loop.run_return(|event, _, control_flow| { + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + }); + drop(window); + + let _window_2 = WindowBuilder::new() + .with_title("A second, fantasticer window!") + .build(&event_loop) + .unwrap(); + + println!("Wa ha ha! You thought that closing the window would finish this?!"); + event_loop.run_return(|event, _, control_flow| { + match event { + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => *control_flow = ControlFlow::Exit, + _ => *control_flow = ControlFlow::Wait, + } + }); + + println!("Okay we're done now for real."); +} diff --git a/src/dpi.rs b/src/dpi.rs index 0d1514273f3..cae9b4012cc 100644 --- a/src/dpi.rs +++ b/src/dpi.rs @@ -33,9 +33,9 @@ //! windows. This event is sent any time the DPI factor changes, either because the window moved to another monitor, //! or because the user changed the configuration of their screen. //! - You can also retrieve the DPI factor of a monitor by calling -//! [`MonitorId::get_hidpi_factor`](../struct.MonitorId.html#method.get_hidpi_factor), or the +//! [`MonitorHandle::get_hidpi_factor`](../monitor/struct.MonitorHandle.html#method.get_hidpi_factor), or the //! current DPI factor applied to a window by calling -//! [`Window::get_hidpi_factor`](../struct.Window.html#method.get_hidpi_factor), which is roughly equivalent +//! [`Window::get_hidpi_factor`](../window/struct.Window.html#method.get_hidpi_factor), which is roughly equivalent //! to `window.get_current_monitor().get_hidpi_factor()`. //! //! Depending on the platform, the window's actual DPI factor may only be known after @@ -59,9 +59,9 @@ //! //! The window's logical size is conserved across DPI changes, resulting in the physical size changing instead. This //! may be surprising on X11, but is quite standard elsewhere. Physical size changes always produce a -//! [`Resized`](../enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs, +//! [`Resized`](../event/enum.WindowEvent.html#variant.Resized) event, even on platforms where no resize actually occurs, //! such as macOS and Wayland. As a result, it's not necessary to separately handle -//! [`HiDpiFactorChanged`](../enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size. +//! [`HiDpiFactorChanged`](../event/enum.WindowEvent.html#variant.HiDpiFactorChanged) if you're only listening for size. //! //! Your GPU has no awareness of the concept of logical pixels, and unless you like wasting pixel density, your //! framebuffer's size should be in physical pixels. diff --git a/src/events.rs b/src/event.rs similarity index 75% rename from src/events.rs rename to src/event.rs index 69a8ec78aa8..1d1a8fb5ba9 100644 --- a/src/events.rs +++ b/src/event.rs @@ -1,26 +1,88 @@ +//! The `Event` enum and assorted supporting types. +//! +//! These are sent to the closure given to [`EventLoop::run(...)`][event_loop_run], where they get +//! processed and used to modify the program state. For more details, see the root-level documentation. +//! +//! [event_loop_run]: ../event_loop/struct.EventLoop.html#method.run +use std::time::Instant; use std::path::PathBuf; -use {DeviceId, LogicalPosition, LogicalSize, WindowId}; +use dpi::{LogicalPosition, LogicalSize}; +use window::WindowId; +use platform_impl; /// Describes a generic event. #[derive(Clone, Debug, PartialEq)] -pub enum Event { +pub enum Event { + /// Emitted when the OS sends an event to a winit window. WindowEvent { window_id: WindowId, event: WindowEvent, }, + /// Emitted when the OS sends an event to a device. DeviceEvent { device_id: DeviceId, event: DeviceEvent, }, - Awakened, - - /// The application has been suspended or resumed. + /// Emitted when an event is sent from [`EventLoopProxy::send_event`](../event_loop/struct.EventLoopProxy.html#method.send_event) + UserEvent(T), + /// Emitted when new events arrive from the OS to be processed. + NewEvents(StartCause), + /// Emitted when all of the event loop's events have been processed and control flow is about + /// to be taken away from the program. + EventsCleared, + + /// Emitted when the event loop is being shut down. This is irreversable - if this event is + /// emitted, it is guaranteed to be the last event emitted. + LoopDestroyed, + + /// Emitted when the application has been suspended or resumed. /// /// The parameter is true if app was suspended, and false if it has been resumed. Suspended(bool), } +impl Event { + pub fn map_nonuser_event(self) -> Result, Event> { + 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)), + } + } +} + +/// Describes the reason the event loop is resuming. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum StartCause { + /// Sent if the time specified by `ControlFlow::WaitUntil` has been reached. Contains the + /// moment the timeout was requested and the requested resume time. The actual resume time is + /// guaranteed to be equal to or after the requested resume time. + ResumeTimeReached { + start: Instant, + requested_resume: Instant + }, + + /// Sent if the OS has new events to send to the window, after a wait was requested. Contains + /// the moment the wait was requested and the resume time, if requested. + WaitCancelled { + start: Instant, + requested_resume: Option + }, + + /// Sent if the event loop is being resumed after the loop's control flow was set to + /// `ControlFlow::Poll`. + Poll, + + /// Sent once, immediately after `run` is called. Indicates that the loop was just initialized. + Init +} + /// Describes an event from a `Window`. #[derive(Clone, Debug, PartialEq)] pub enum WindowEvent { @@ -37,19 +99,19 @@ pub enum WindowEvent { Destroyed, /// A file has been dropped into the window. - /// + /// /// When the user drops multiple files at once, this event will be emitted for each file /// separately. DroppedFile(PathBuf), /// A file is being hovered over the window. - /// + /// /// When the user hovers multiple files at once, this event will be emitted for each file /// separately. HoveredFile(PathBuf), /// A file was hovered, but has exited the window. - /// + /// /// There will be a single `HoveredFileCancelled` event triggered even if multiple files were /// hovered. HoveredFileCancelled, @@ -99,8 +161,8 @@ pub enum WindowEvent { /// Motion on some analog axis. May report data redundant to other, more specific events. AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 }, - /// The window needs to be redrawn. - Refresh, + /// The OS or application has requested that the window be redrawn. + RedrawRequested, /// Touch event has been received Touch(Touch), @@ -117,6 +179,25 @@ pub enum WindowEvent { HiDpiFactorChanged(f64), } +/// Identifier of an input device. +/// +/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which +/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or +/// physical. Virtual devices typically aggregate inputs from multiple physical devices. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DeviceId(pub(crate) platform_impl::DeviceId); + +impl DeviceId { + /// Returns a dummy `DeviceId`, useful for unit testing. The only guarantee made about the return + /// value of this function is that it will always be equal to itself and to future values returned + /// by this function. No other guarantees are made. This may be equal to a real `DeviceId`. + /// + /// **Passing this into a winit function will result in undefined behavior.** + pub unsafe fn dummy() -> Self { + DeviceId(platform_impl::DeviceId::dummy()) + } +} + /// Represents raw hardware events that are not associated with any particular window. /// /// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person diff --git a/src/event_loop.rs b/src/event_loop.rs new file mode 100644 index 00000000000..ca217f7c97a --- /dev/null +++ b/src/event_loop.rs @@ -0,0 +1,203 @@ +//! The `EventLoop` struct and assorted supporting types, including `ControlFlow`. +//! +//! If you want to send custom events to the event loop, use [`EventLoop::create_proxy()`][create_proxy] +//! to acquire an [`EventLoopProxy`][event_loop_proxy] and call its [`send_event`][send_event] method. +//! +//! See the root-level documentation for information on how to create and use an event loop to +//! handle events. +//! +//! [create_proxy]: ./struct.EventLoop.html#method.create_proxy +//! [event_loop_proxy]: ./struct.EventLoopProxy.html +//! [send_event]: ./struct.EventLoopProxy.html#method.send_event +use std::{fmt, error}; +use std::time::Instant; +use std::ops::Deref; + +use platform_impl; +use event::Event; +use monitor::{AvailableMonitorsIter, MonitorHandle}; + +/// Provides a way to retrieve events from the system and from the windows that were registered to +/// the events loop. +/// +/// An `EventLoop` can be seen more or less as a "context". Calling `EventLoop::new()` +/// initializes everything that will be required to create windows. For example on Linux creating +/// an events loop opens a connection to the X or Wayland server. +/// +/// To wake up an `EventLoop` from a another thread, see the `EventLoopProxy` docs. +/// +/// Note that the `EventLoop` cannot be shared across threads (due to platform-dependant logic +/// forbidding 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 +/// `EventLoopProxy` allows you to wake up an `EventLoop` from an other thread. +pub struct EventLoop { + pub(crate) event_loop: platform_impl::EventLoop, + pub(crate) _marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync +} + +/// Target that associates windows with an `EventLoop`. +/// +/// This type exists to allow you to create new windows while Winit executes your callback. +/// `EventLoop` will coerce into this type, so functions that take this as a parameter can also +/// take `&EventLoop`. +pub struct EventLoopWindowTarget { + pub(crate) p: platform_impl::EventLoopWindowTarget, + pub(crate) _marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync +} + +impl fmt::Debug for EventLoop { + fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { + fmtr.pad("EventLoop { .. }") + } +} + +impl fmt::Debug for EventLoopWindowTarget { + fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { + fmtr.pad("EventLoopWindowTarget { .. }") + } +} + +/// Set by the user callback given to the `EventLoop::run` method. +/// +/// Indicates the desired behavior of the event loop after [`Event::EventsCleared`][events_cleared] +/// is emitted. Defaults to `Poll`. +/// +/// ## Persistency +/// Almost every change is persistent between multiple calls to the event loop closure within a +/// given run loop. The only exception to this is `Exit` which, once set, cannot be unset. Changes +/// are **not** persistent between multiple calls to `run_return` - issuing a new call will reset +/// the control flow to `Poll`. +/// +/// [events_cleared]: ../event/enum.Event.html#variant.EventsCleared +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum ControlFlow { + /// When the current loop iteration finishes, immediately begin a new iteration regardless of + /// whether or not new events are available to process. + Poll, + /// When the current loop iteration finishes, suspend the thread until another event arrives. + Wait, + /// When the current loop iteration finishes, suspend the thread until either another event + /// arrives or the given time is reached. + WaitUntil(Instant), + /// Send a `LoopDestroyed` event and stop the event loop. This variant is *sticky* - once set, + /// `control_flow` cannot be changed from `Exit`, and any future attempts to do so will result + /// in the `control_flow` parameter being reset to `Exit`. + Exit +} + +impl Default for ControlFlow { + #[inline(always)] + fn default() -> ControlFlow { + ControlFlow::Poll + } +} + +impl EventLoop<()> { + /// Builds a new event loop with a `()` as the user event type. + pub fn new() -> EventLoop<()> { + EventLoop::<()>::new_user_event() + } +} + +impl EventLoop { + /// Builds a new event loop. + /// + /// 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 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 fn new_user_event() -> EventLoop { + EventLoop { + event_loop: platform_impl::EventLoop::new(), + _marker: ::std::marker::PhantomData, + } + } + + /// Returns the list of all the monitors available on the system. + /// + // Note: should be replaced with `-> impl Iterator` once stable. + #[inline] + pub fn get_available_monitors(&self) -> AvailableMonitorsIter { + let data = self.event_loop.get_available_monitors(); + AvailableMonitorsIter{ data: data.into_iter() } + } + + /// Returns the primary monitor of the system. + #[inline] + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle { inner: self.event_loop.get_primary_monitor() } + } + + /// Hijacks the calling thread and initializes the `winit` event loop with the provided + /// closure. Since the closure is `'static`, it must be a `move` closure if it needs to + /// access any data from the calling context. + /// + /// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the + /// event loop's behavior. + /// + /// Any values not passed to this function will *not* be dropped. + /// + /// [`ControlFlow`]: ./enum.ControlFlow.html + #[inline] + pub fn run(self, event_handler: F) -> ! + where F: 'static + FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow) + { + self.event_loop.run(event_handler) + } + + /// Creates an `EventLoopProxy` that can be used to wake up the `EventLoop` from another + /// thread. + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { + event_loop_proxy: self.event_loop.create_proxy(), + } + } +} + +impl Deref for EventLoop { + type Target = EventLoopWindowTarget; + fn deref(&self) -> &EventLoopWindowTarget { + self.event_loop.window_target() + } +} + +/// Used to send custom events to `EventLoop`. +#[derive(Clone)] +pub struct EventLoopProxy { + event_loop_proxy: platform_impl::EventLoopProxy, +} + +impl EventLoopProxy { + /// 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 + /// function. + /// + /// Returns an `Err` if the associated `EventLoop` no longer exists. + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + self.event_loop_proxy.send_event(event) + } +} + +impl fmt::Debug for EventLoopProxy { + fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { + fmtr.pad("EventLoopProxy { .. }") + } +} + +/// The error that is returned when an `EventLoopProxy` attempts to wake up an `EventLoop` that +/// no longer exists. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct EventLoopClosed; + +impl fmt::Display for EventLoopClosed { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", error::Error::description(self)) + } +} + +impl error::Error for EventLoopClosed { + fn description(&self) -> &str { + "Tried to wake up a closed `EventLoop`" + } +} + diff --git a/src/lib.rs b/src/lib.rs index 420d26586be..140ed8edb5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,87 +2,78 @@ //! //! # Building a window //! -//! Before you can build a window, you first need to build an `EventsLoop`. This is done with the -//! `EventsLoop::new()` function. Example: +//! Before you can build a [`Window`], you first need to build an [`EventLoop`]. This is done with the +//! [`EventLoop::new()`] function. //! //! ```no_run -//! use winit::EventsLoop; -//! let events_loop = EventsLoop::new(); +//! use winit::event_loop::EventLoop; +//! let event_loop = EventLoop::new(); //! ``` //! -//! Once this is done there are two ways to create a window: +//! Once this is done there are two ways to create a [`Window`]: //! -//! - Calling `Window::new(&events_loop)`. -//! - Calling `let builder = WindowBuilder::new()` then `builder.build(&events_loop)`. +//! - Calling [`Window::new(&event_loop)`][window_new]. +//! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build]. //! //! The first way is the simplest way and will give you default values for everything. //! -//! The second way allows you to customize the way your window will look and behave by modifying -//! the fields of the `WindowBuilder` object before you create the window. +//! The second way allows you to customize the way your [`Window`] will look and behave by modifying +//! the fields of the [`WindowBuilder`] object before you create the [`Window`]. //! -//! # Events handling +//! # Event handling //! -//! Once a window has been created, it will *generate events*. For example whenever the user moves -//! the window, resizes the window, moves the mouse, etc. an event is generated. +//! Once a [`Window`] has been created, it will *generate events*. For example whenever the user moves +//! the [`Window`], resizes the [`Window`], moves the mouse, etc. an event is generated. //! -//! The events generated by a window can be retrieved from the `EventsLoop` the window was created +//! The events generated by a [`Window`] can be retreived from the [`EventLoop`] the [`Window`] was created //! with. //! -//! There are two ways to do so. The first is to call `events_loop.poll_events(...)`, which will -//! retrieve all the events pending on the windows and immediately return after no new event is -//! available. You usually want to use this method in application that render continuously on the -//! screen, such as video games. +//! You do this by calling [`event_loop.run(...)`][event_loop_run]. This function will run forever +//! unless `control_flow` is set to [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`] +//! is emitted and the entire program terminates. //! //! ```no_run -//! use winit::{Event, WindowEvent}; -//! use winit::dpi::LogicalSize; -//! # use winit::EventsLoop; -//! # let mut events_loop = EventsLoop::new(); +//! use winit::event_loop::ControlFlow; +//! use winit::event::{Event, WindowEvent}; +//! # use winit::event_loop::EventLoop; +//! # let event_loop = EventLoop::new(); //! -//! loop { -//! events_loop.poll_events(|event| { -//! match event { -//! Event::WindowEvent { -//! event: WindowEvent::Resized(LogicalSize { width, height }), -//! .. -//! } => { -//! println!("The window was resized to {}x{}", width, height); -//! }, -//! _ => () -//! } -//! }); -//! } -//! ``` -//! -//! The second way is to call `events_loop.run_forever(...)`. As its name tells, it will run -//! forever unless it is stopped by returning `ControlFlow::Break`. -//! -//! ```no_run -//! use winit::{ControlFlow, Event, WindowEvent}; -//! # use winit::EventsLoop; -//! # let mut events_loop = EventsLoop::new(); -//! -//! events_loop.run_forever(|event| { +//! event_loop.run(move |event, _, control_flow| { //! match event { //! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { //! println!("The close button was pressed; stopping"); -//! ControlFlow::Break +//! *control_flow = ControlFlow::Exit //! }, -//! _ => ControlFlow::Continue, +//! _ => *control_flow = ControlFlow::Wait, //! } //! }); //! ``` //! -//! If you use multiple windows, the `WindowEvent` event has a member named `window_id`. You can -//! compare it with the value returned by the `id()` method of `Window` in order to know which -//! window has received the event. +//! If you use multiple [`Window`]s, [`Event`]`::`[`WindowEvent`] has a member named `window_id`. You can +//! compare it with the value returned by the [`id()`][window_id_fn] method of [`Window`] in order to know which +//! [`Window`] has received the event. //! //! # Drawing on the window //! -//! Winit doesn't provide any function that allows drawing on a window. However it allows you to -//! retrieve the raw handle of the window (see the `os` module for that), which in turn allows you -//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the window. -//! +//! Winit doesn't provide any function that allows drawing on a [`Window`]. However it allows you to +//! retrieve the raw handle of the window (see the [`platform`] module), which in turn allows you +//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw on the [`Window`]. +//! +//! [`EventLoop`]: ./event_loop/struct.EventLoop.html +//! [`EventLoop::new()`]: ./event_loop/struct.EventLoop.html#method.new +//! [event_loop_run]: ./event_loop/struct.EventLoop.html#method.run +//! [`ControlFlow`]: ./event_loop/enum.ControlFlow.html +//! [`Exit`]: ./event_loop/enum.ControlFlow.html#variant.Exit +//! [`Window`]: ./window/struct.Window.html +//! [`WindowBuilder`]: ./window/struct.WindowBuilder.html +//! [window_new]: ./window/struct.Window.html#method.new +//! [window_builder_new]: ./window/struct.WindowBuilder.html#method.new +//! [window_builder_build]: ./window/struct.WindowBuilder.html#method.build +//! [window_id_fn]: ./window/struct.Window.html#method.id +//! [`Event`]: ./event/enum.Event.html +//! [`WindowEvent`]: ./event/enum.Event.html#variant.WindowEvent +//! [`LoopDestroyed`]: ./event/enum.Event.html#variant.LoopDestroyed +//! [`platform`]: ./platform/index.html #[allow(unused_imports)] #[macro_use] @@ -114,7 +105,7 @@ extern crate core_foundation; extern crate core_graphics; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] extern crate x11_dl; -#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "windows"))] extern crate parking_lot; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] extern crate percent_encoding; @@ -125,422 +116,12 @@ extern crate drm; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] extern crate gbm; -pub(crate) use dpi::*; // TODO: Actually change the imports throughout the codebase. -pub use events::*; -pub use window::{AvailableMonitorsIter, MonitorId}; -pub use icon::*; - pub mod dpi; -mod events; +pub mod event; +pub mod event_loop; mod icon; -mod platform; -mod window; - -pub mod os; - -/// Represents a window. -/// -/// # Example -/// -/// ```no_run -/// use winit::{Event, EventsLoop, Window, WindowEvent, ControlFlow}; -/// -/// let mut events_loop = EventsLoop::new(); -/// let window = Window::new(&events_loop).unwrap(); -/// -/// events_loop.run_forever(|event| { -/// match event { -/// Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { -/// ControlFlow::Break -/// }, -/// _ => ControlFlow::Continue, -/// } -/// }); -/// ``` -pub struct Window { - window: platform::Window, -} - -impl std::fmt::Debug for Window { - fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result { - fmtr.pad("Window { .. }") - } -} - -/// Identifier of a window. Unique for each window. -/// -/// Can be obtained with `window.id()`. -/// -/// Whenever you receive an event specific to a window, this event contains a `WindowId` which you -/// can then compare to the ids of your windows. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId(platform::WindowId); - -impl WindowId { - /// Returns a dummy `WindowId`, useful for unit testing. The only guarantee made about the return - /// value of this function is that it will always be equal to itself and to future values returned - /// by this function. No other guarantees are made. This may be equal to a real `WindowId`. - /// - /// **Passing this into a winit function will result in undefined behavior.** - pub unsafe fn dummy() -> Self { - WindowId(platform::WindowId::dummy()) - } -} - -/// Identifier of an input device. -/// -/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which -/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or -/// physical. Virtual devices typically aggregate inputs from multiple physical devices. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(platform::DeviceId); - -impl DeviceId { - /// Returns a dummy `DeviceId`, useful for unit testing. The only guarantee made about the return - /// value of this function is that it will always be equal to itself and to future values returned - /// by this function. No other guarantees are made. This may be equal to a real `DeviceId`. - /// - /// **Passing this into a winit function will result in undefined behavior.** - pub unsafe fn dummy() -> Self { - DeviceId(platform::DeviceId::dummy()) - } -} - -/// Provides a way to retrieve events from the system and from the windows that were registered to -/// the events loop. -/// -/// An `EventsLoop` can be seen more or less as a "context". Calling `EventsLoop::new()` -/// initializes everything that will be required to create windows. For example on Linux creating -/// an events loop opens a connection to the X or Wayland server. -/// -/// To wake up an `EventsLoop` from a another thread, see the `EventsLoopProxy` docs. -/// -/// Note that the `EventsLoop` cannot be shared accross threads (due to platform-dependant logic -/// forbiding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the -/// `Window` created from this `EventsLoop` _can_ be sent to an other thread, and the -/// `EventsLoopProxy` allows you to wakeup an `EventsLoop` from an other thread. -pub struct EventsLoop { - events_loop: platform::EventsLoop, - _marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync -} - -impl std::fmt::Debug for EventsLoop { - fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result { - fmtr.pad("EventsLoop { .. }") - } -} - -/// Returned by the user callback given to the `EventsLoop::run_forever` method. -/// -/// Indicates whether the `run_forever` method should continue or complete. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum ControlFlow { - /// Continue looping and waiting for events. - Continue, - /// Break from the event loop. - Break, -} - -impl EventsLoop { - /// Builds a new events loop. - /// - /// 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 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 fn new() -> EventsLoop { - EventsLoop { - events_loop: platform::EventsLoop::new(), - _marker: ::std::marker::PhantomData, - } - } - - /// Returns the list of all the monitors available on the system. - /// - // Note: should be replaced with `-> impl Iterator` once stable. - #[inline] - pub fn get_available_monitors(&self) -> AvailableMonitorsIter { - let data = self.events_loop.get_available_monitors(); - AvailableMonitorsIter{ data: data.into_iter() } - } - - /// Returns the primary monitor of the system. - #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId { inner: self.events_loop.get_primary_monitor() } - } - - /// Fetches all the events that are pending, calls the callback function for each of them, - /// and returns. - #[inline] - pub fn poll_events(&mut self, callback: F) - where F: FnMut(Event) - { - self.events_loop.poll_events(callback) - } - - /// Calls `callback` every time an event is received. If no event is available, sleeps the - /// current thread and waits for an event. If the callback returns `ControlFlow::Break` then - /// `run_forever` will immediately return. - /// - /// # Danger! - /// - /// The callback is run after *every* event, so if its execution time is non-trivial the event queue may not empty - /// at a sufficient rate. Rendering in the callback with vsync enabled **will** cause significant lag. - #[inline] - pub fn run_forever(&mut self, callback: F) - where F: FnMut(Event) -> ControlFlow - { - self.events_loop.run_forever(callback) - } - - /// Creates an `EventsLoopProxy` that can be used to wake up the `EventsLoop` from another - /// thread. - pub fn create_proxy(&self) -> EventsLoopProxy { - EventsLoopProxy { - events_loop_proxy: self.events_loop.create_proxy(), - } - } -} - -/// Used to wake up the `EventsLoop` from another thread. -#[derive(Clone)] -pub struct EventsLoopProxy { - events_loop_proxy: platform::EventsLoopProxy, -} - -impl std::fmt::Debug for EventsLoopProxy { - fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result { - fmtr.pad("EventsLoopProxy { .. }") - } -} - -impl EventsLoopProxy { - /// Wake up the `EventsLoop` from which this proxy was created. - /// - /// This causes the `EventsLoop` to emit an `Awakened` event. - /// - /// Returns an `Err` if the associated `EventsLoop` no longer exists. - pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { - self.events_loop_proxy.wakeup() - } -} - -/// The error that is returned when an `EventsLoopProxy` attempts to wake up an `EventsLoop` that -/// no longer exists. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct EventsLoopClosed; - -impl std::fmt::Display for EventsLoopClosed { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", std::error::Error::description(self)) - } -} - -impl std::error::Error for EventsLoopClosed { - fn description(&self) -> &str { - "Tried to wake up a closed `EventsLoop`" - } -} - -/// Object that allows you to build windows. -#[derive(Clone)] -pub struct WindowBuilder { - /// The attributes to use to create the window. - pub window: WindowAttributes, - - // Platform-specific configuration. Private. - platform_specific: platform::PlatformSpecificWindowBuilderAttributes, -} - -impl std::fmt::Debug for WindowBuilder { - fn fmt(&self, fmtr: &mut std::fmt::Formatter) -> std::fmt::Result { - fmtr.debug_struct("WindowBuilder") - .field("window", &self.window) - .finish() - } -} - -/// Error that can happen while creating a window or a headless renderer. -#[derive(Debug, Clone)] -pub enum CreationError { - OsError(String), - /// TODO: remove this error - NotSupported, -} - -impl CreationError { - fn to_string(&self) -> &str { - match *self { - CreationError::OsError(ref text) => &text, - CreationError::NotSupported => "Some of the requested attributes are not supported", - } - } -} - -impl std::fmt::Display for CreationError { - fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - formatter.write_str(self.to_string()) - } -} - -impl std::error::Error for CreationError { - fn description(&self) -> &str { - self.to_string() - } -} - -/// Describes the appearance of the mouse cursor. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum MouseCursor { - /// The platform-dependent default cursor. - Default, - /// A simple crosshair. - Crosshair, - /// A hand (often used to indicate links in web browsers). - Hand, - /// Self explanatory. - Arrow, - /// Indicates something is to be moved. - Move, - /// Indicates text that may be selected or edited. - Text, - /// Program busy indicator. - Wait, - /// Help indicator (often rendered as a "?") - Help, - /// Progress indicator. Shows that processing is being done. But in contrast - /// with "Wait" the user may still interact with the program. Often rendered - /// as a spinning beach ball, or an arrow with a watch or hourglass. - Progress, - - /// Cursor showing that something cannot be done. - NotAllowed, - ContextMenu, - Cell, - VerticalText, - Alias, - Copy, - NoDrop, - Grab, - Grabbing, - AllScroll, - ZoomIn, - ZoomOut, - - /// Indicate that some edge is to be moved. For example, the 'SeResize' cursor - /// is used when the movement starts from the south-east corner of the box. - EResize, - NResize, - NeResize, - NwResize, - SResize, - SeResize, - SwResize, - WResize, - EwResize, - NsResize, - NeswResize, - NwseResize, - ColResize, - RowResize, -} - -impl Default for MouseCursor { - fn default() -> Self { - MouseCursor::Default - } -} - -/// Attributes to use when creating a window. -#[derive(Debug, Clone)] -pub struct WindowAttributes { - /// The dimensions of the window. If this is `None`, some platform-specific dimensions will be - /// used. - /// - /// The default is `None`. - pub dimensions: Option, - - /// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved). - /// - /// The default is `None`. - pub min_dimensions: Option, - - /// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform. - /// - /// The default is `None`. - pub max_dimensions: Option, - - /// Whether the window is resizable or not. - /// - /// The default is `true`. - pub resizable: bool, - - /// Whether the window should be set as fullscreen upon creation. - /// - /// The default is `None`. - pub fullscreen: Option, - - /// The title of the window in the title bar. - /// - /// The default is `"winit window"`. - pub title: String, - - /// Whether the window should be maximized upon creation. - /// - /// The default is `false`. - pub maximized: bool, - - /// Whether the window should be immediately visible upon creation. - /// - /// The default is `true`. - pub visible: bool, - - /// Whether the the window should be transparent. If this is true, writing colors - /// with alpha values different than `1.0` will produce a transparent window. - /// - /// The default is `false`. - pub transparent: bool, - - /// Whether the window should have borders and bars. - /// - /// The default is `true`. - pub decorations: bool, - - /// Whether the window should always be on top of other windows. - /// - /// The default is `false`. - pub always_on_top: bool, - - /// The window icon. - /// - /// The default is `None`. - pub window_icon: Option, - - /// [iOS only] Enable multitouch, - /// see [multipleTouchEnabled](https://developer.apple.com/documentation/uikit/uiview/1622519-multipletouchenabled) - pub multitouch: bool, -} +mod platform_impl; +pub mod window; +pub mod monitor; -impl Default for WindowAttributes { - #[inline] - fn default() -> WindowAttributes { - WindowAttributes { - dimensions: None, - min_dimensions: None, - max_dimensions: None, - resizable: true, - title: "winit window".to_owned(), - maximized: false, - fullscreen: None, - visible: true, - transparent: false, - decorations: true, - always_on_top: false, - window_icon: None, - multitouch: false, - } - } -} +pub mod platform; diff --git a/src/monitor.rs b/src/monitor.rs new file mode 100644 index 00000000000..7533cb78dd8 --- /dev/null +++ b/src/monitor.rs @@ -0,0 +1,91 @@ +//! Types useful for interacting with a user's monitors. +//! +//! If you want to get basic information about a monitor, you can use the [`MonitorHandle`][monitor_id] +//! type. This is retreived from an [`AvailableMonitorsIter`][monitor_iter], which can be acquired +//! with: +//! - [`EventLoop::get_available_monitors`][loop_get] +//! - [`Window::get_available_monitors`][window_get]. +//! +//! [monitor_id]: ./struct.MonitorHandle.html +//! [monitor_iter]: ./struct.AvailableMonitorsIter.html +//! [loop_get]: ../event_loop/struct.EventLoop.html#method.get_available_monitors +//! [window_get]: ../window/struct.Window.html#method.get_available_monitors +use std::collections::vec_deque::IntoIter as VecDequeIter; + +use platform_impl; +use dpi::{PhysicalPosition, PhysicalSize}; + +/// An iterator over all available monitors. +/// +/// Can be acquired with: +/// - [`EventLoop::get_available_monitors`][loop_get] +/// - [`Window::get_available_monitors`][window_get]. +/// +/// [loop_get]: ../event_loop/struct.EventLoop.html#method.get_available_monitors +/// [window_get]: ../window/struct.Window.html#method.get_available_monitors +// Implementation note: we retrieve the list once, then serve each element by one by one. +// This may change in the future. +#[derive(Debug)] +pub struct AvailableMonitorsIter { + pub(crate) data: VecDequeIter, +} + +impl Iterator for AvailableMonitorsIter { + type Item = MonitorHandle; + + #[inline] + fn next(&mut self) -> Option { + self.data.next().map(|id| MonitorHandle { inner: id }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.data.size_hint() + } +} + +/// Handle to a monitor. +/// +/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation. +/// +/// [`Window`]: ../window/struct.Window.html +#[derive(Debug, Clone)] +pub struct MonitorHandle { + pub(crate) inner: platform_impl::MonitorHandle +} + +impl MonitorHandle { + /// Returns a human-readable name of the monitor. + /// + /// Returns `None` if the monitor doesn't exist anymore. + #[inline] + pub fn get_name(&self) -> Option { + self.inner.get_name() + } + + /// Returns the monitor's resolution. + #[inline] + pub fn get_dimensions(&self) -> PhysicalSize { + self.inner.get_dimensions() + } + + /// Returns the top-left corner position of the monitor relative to the larger full + /// screen area. + #[inline] + pub fn get_position(&self) -> PhysicalPosition { + self.inner.get_position() + } + + /// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa. + /// + /// See the [`dpi`](dpi/index.html) module for more information. + /// + /// ## Platform-specific + /// + /// - **X11:** Can be overridden using the `WINIT_HIDPI_FACTOR` environment variable. + /// - **Android:** Always returns 1.0. + #[inline] + pub fn get_hidpi_factor(&self) -> f64 { + self.inner.get_hidpi_factor() + } +} diff --git a/src/os/mod.rs b/src/os/mod.rs deleted file mode 100644 index 2496769563f..00000000000 --- a/src/os/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Contains traits with platform-specific methods in them. -//! -//! Contains the follow modules: -//! -//! - `android` -//! - `ios` -//! - `macos` -//! - `unix` -//! - `windows` -//! -//! However only the module corresponding to the platform you're compiling to will be available. -//! -pub mod android; -pub mod ios; -pub mod macos; -pub mod unix; -pub mod windows; diff --git a/src/os/android.rs b/src/platform/android.rs similarity index 64% rename from src/os/android.rs rename to src/platform/android.rs index 06661a325e2..37ee0e1d4ba 100644 --- a/src/os/android.rs +++ b/src/platform/android.rs @@ -1,28 +1,28 @@ #![cfg(any(target_os = "android"))] use std::os::raw::c_void; -use EventsLoop; +use EventLoop; use Window; use WindowBuilder; -/// Additional methods on `EventsLoop` that are specific to Android. -pub trait EventsLoopExt { +/// Additional methods on `EventLoop` that are specific to Android. +pub trait EventLoopExtAndroid { /// Makes it possible for glutin to register a callback when a suspend event happens on Android fn set_suspend_callback(&self, cb: Option ()>>); } -impl EventsLoopExt for EventsLoop { +impl EventLoopExtAndroid for EventLoop { fn set_suspend_callback(&self, cb: Option ()>>) { - self.events_loop.set_suspend_callback(cb); + self.event_loop.set_suspend_callback(cb); } } /// Additional methods on `Window` that are specific to Android. -pub trait WindowExt { +pub trait WindowExtAndroid { fn get_native_window(&self) -> *const c_void; } -impl WindowExt for Window { +impl WindowExtAndroid for Window { #[inline] fn get_native_window(&self) -> *const c_void { self.window.get_native_window() @@ -30,9 +30,9 @@ impl WindowExt for Window { } /// Additional methods on `WindowBuilder` that are specific to Android. -pub trait WindowBuilderExt { +pub trait WindowBuilderExtAndroid { } -impl WindowBuilderExt for WindowBuilder { +impl WindowBuilderExtAndroid for WindowBuilder { } diff --git a/src/platform/desktop.rs b/src/platform/desktop.rs new file mode 100644 index 00000000000..057838903ea --- /dev/null +++ b/src/platform/desktop.rs @@ -0,0 +1,31 @@ +#![cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd" +))] + +use event::Event; +use event_loop::{EventLoop, EventLoopWindowTarget, ControlFlow}; + +/// Additional methods on `EventLoop` that are specific to desktop platforms. +pub trait EventLoopExtDesktop { + /// A type provided by the user that can be passed through `Event::UserEvent`. + type UserEvent; + + /// Initializes the `winit` event loop. + /// + /// Unlike `run`, this function accepts non-`'static` (i.e. non-`move`) closures and returns + /// control flow to the caller when `control_flow` is set to `ControlFlow::Exit`. + fn run_return(&mut self, event_handler: F) + where F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow); +} + +impl EventLoopExtDesktop for EventLoop { + type UserEvent = T; + + fn run_return(&mut self, event_handler: F) + where F: FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow) + { + self.event_loop.run_return(event_handler) + } +} diff --git a/src/os/ios.rs b/src/platform/ios.rs similarity index 82% rename from src/os/ios.rs rename to src/platform/ios.rs index 62c2c2470fe..7a2345b044f 100644 --- a/src/os/ios.rs +++ b/src/platform/ios.rs @@ -2,10 +2,10 @@ use std::os::raw::c_void; -use {MonitorId, Window, WindowBuilder}; +use {MonitorHandle, Window, WindowBuilder}; /// Additional methods on `Window` that are specific to iOS. -pub trait WindowExt { +pub trait WindowExtIOS { /// Returns a pointer to the `UIWindow` that is used by this window. /// /// The pointer will become invalid when the `Window` is destroyed. @@ -17,7 +17,7 @@ pub trait WindowExt { fn get_uiview(&self) -> *mut c_void; } -impl WindowExt for Window { +impl WindowExtIOS for Window { #[inline] fn get_uiwindow(&self) -> *mut c_void { self.window.get_uiwindow() as _ @@ -30,14 +30,14 @@ impl WindowExt for Window { } /// Additional methods on `WindowBuilder` that are specific to iOS. -pub trait WindowBuilderExt { +pub trait WindowBuilderExtIOS { /// Sets the root view class used by the `Window`, otherwise a barebones `UIView` is provided. /// /// The class will be initialized by calling `[root_view initWithFrame:CGRect]` fn with_root_view_class(self, root_view_class: *const c_void) -> WindowBuilder; } -impl WindowBuilderExt for WindowBuilder { +impl WindowBuilderExtIOS for WindowBuilder { #[inline] fn with_root_view_class(mut self, root_view_class: *const c_void) -> WindowBuilder { self.platform_specific.root_view_class = unsafe { &*(root_view_class as *const _) }; @@ -45,13 +45,13 @@ impl WindowBuilderExt for WindowBuilder { } } -/// Additional methods on `MonitorId` that are specific to iOS. -pub trait MonitorIdExt { +/// Additional methods on `MonitorHandle` that are specific to iOS. +pub trait MonitorHandleExtIOS { /// Returns a pointer to the `UIScreen` that is used by this monitor. fn get_uiscreen(&self) -> *mut c_void; } -impl MonitorIdExt for MonitorId { +impl MonitorHandleExtIOS for MonitorHandle { #[inline] fn get_uiscreen(&self) -> *mut c_void { self.inner.get_uiscreen() as _ diff --git a/src/os/macos.rs b/src/platform/macos.rs similarity index 94% rename from src/os/macos.rs rename to src/platform/macos.rs index b9ea993b392..8997e3b2657 100644 --- a/src/os/macos.rs +++ b/src/platform/macos.rs @@ -1,10 +1,10 @@ #![cfg(target_os = "macos")] use std::os::raw::c_void; -use {LogicalSize, MonitorId, Window, WindowBuilder}; +use {LogicalSize, MonitorHandle, Window, WindowBuilder}; /// Additional methods on `Window` that are specific to MacOS. -pub trait WindowExt { +pub trait WindowExtMacOS { /// Returns a pointer to the cocoa `NSWindow` that is used by this window. /// /// The pointer will become invalid when the `Window` is destroyed. @@ -33,7 +33,7 @@ pub trait WindowExt { fn set_simple_fullscreen(&self, fullscreen: bool) -> bool; } -impl WindowExt for Window { +impl WindowExtMacOS for Window { #[inline] fn get_nswindow(&self) -> *mut c_void { self.window.get_nswindow() @@ -82,7 +82,7 @@ impl Default for ActivationPolicy { /// - `with_titlebar_hidden` /// - `with_titlebar_buttons_hidden` /// - `with_fullsize_content_view` -pub trait WindowBuilderExt { +pub trait WindowBuilderExtMacOS { /// Sets the activation policy for the window being built. fn with_activation_policy(self, activation_policy: ActivationPolicy) -> WindowBuilder; /// Enables click-and-drag behavior for the entire window, not just the titlebar. @@ -101,7 +101,7 @@ pub trait WindowBuilderExt { fn with_resize_increments(self, increments: LogicalSize) -> WindowBuilder; } -impl WindowBuilderExt for WindowBuilder { +impl WindowBuilderExtMacOS for WindowBuilder { #[inline] fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder { self.platform_specific.activation_policy = activation_policy; @@ -151,15 +151,15 @@ impl WindowBuilderExt for WindowBuilder { } } -/// Additional methods on `MonitorId` that are specific to MacOS. -pub trait MonitorIdExt { +/// Additional methods on `MonitorHandle` that are specific to MacOS. +pub trait MonitorHandleExtMacOS { /// Returns the identifier of the monitor for Cocoa. fn native_id(&self) -> u32; /// Returns a pointer to the NSScreen representing this monitor. fn get_nsscreen(&self) -> Option<*mut c_void>; } -impl MonitorIdExt for MonitorId { +impl MonitorHandleExtMacOS for MonitorHandle { #[inline] fn native_id(&self) -> u32 { self.inner.get_native_identifier() diff --git a/src/platform/mod.rs b/src/platform/mod.rs index be968785004..ba494ac6d36 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,26 +1,23 @@ -pub use self::platform::*; +//! Contains traits with platform-specific methods in them. +//! +//! Contains the follow OS-specific modules: +//! +//! - `android` +//! - `ios` +//! - `macos` +//! - `unix` +//! - `windows` +//! +//! And the following platform-specific module: +//! +//! - `desktop` (available on `windows`, `unix`, and `macos`) +//! +//! However only the module corresponding to the platform you're compiling to will be available. -#[cfg(target_os = "windows")] -#[path="windows/mod.rs"] -mod platform; -#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] -#[path="linux/mod.rs"] -mod platform; -#[cfg(target_os = "macos")] -#[path="macos/mod.rs"] -mod platform; -#[cfg(target_os = "android")] -#[path="android/mod.rs"] -mod platform; -#[cfg(target_os = "ios")] -#[path="ios/mod.rs"] -mod platform; -#[cfg(target_os = "emscripten")] -#[path="emscripten/mod.rs"] -mod platform; +pub mod android; +pub mod ios; +pub mod macos; +pub mod unix; +pub mod windows; -#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"), - not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"), - not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd"), - not(target_os = "emscripten")))] -compile_error!("The platform you're compiling for is not supported by winit"); +pub mod desktop; diff --git a/src/os/unix.rs b/src/platform/unix.rs similarity index 92% rename from src/os/unix.rs rename to src/platform/unix.rs index da4c8a3730b..00d362befe7 100644 --- a/src/os/unix.rs +++ b/src/platform/unix.rs @@ -7,25 +7,25 @@ use std::sync::Arc; use sctk::window::{ButtonState, Theme}; use { - EventsLoop, + EventLoop, LogicalSize, - MonitorId, + MonitorHandle, Window, WindowBuilder, }; -use platform::{ - EventsLoop as LinuxEventsLoop, +use platform_impl::{ + EventLoop as LinuxEventLoop, Window as LinuxWindow, }; -use platform::x11::XConnection; -use platform::x11::ffi::XVisualInfo; +use platform_impl::x11::XConnection; +use platform_impl::x11::ffi::XVisualInfo; // TODO: stupid hack so that glutin can do its work #[doc(hidden)] -pub use platform::x11; +pub use platform_impl::x11; -pub use platform::XNotSupported; -pub use platform::x11::util::WindowType as XWindowType; +pub use platform_impl::XNotSupported; +pub use platform_impl::x11::util::WindowType as XWindowType; /// Theme for wayland client side decorations /// @@ -95,20 +95,20 @@ impl Theme for WaylandThemeObject { } } -/// Additional methods on `EventsLoop` that are specific to Linux. -pub trait EventsLoopExt { +/// Additional methods on `EventLoop` that are specific to Unix. +pub trait EventLoopExtUnix { /// Builds a new `EventsLoop` that is forced to use X11. fn new_x11() -> Result where Self: Sized; - /// Builds a new `EventsLoop` that is forced to use Wayland. + /// Builds a new `EventLoop` that is forced to use Wayland. fn new_wayland() -> Self where Self: Sized; - /// True if the `EventsLoop` uses Wayland. + /// True if the `EventLoop` uses Wayland. fn is_wayland(&self) -> bool; - /// True if the `EventsLoop` uses X11. + /// True if the `EventLoop` uses X11. fn is_x11(&self) -> bool; /// True if the `EventsLoop` uses Gbm. @@ -127,12 +127,12 @@ pub trait EventsLoopExt { fn get_gbm_display(&self) -> Option<*mut raw::c_void>; } -impl EventsLoopExt for EventsLoop { +impl EventLoopExtUnix for EventLoop { #[inline] fn new_x11() -> Result { - LinuxEventsLoop::new_x11().map(|ev| - EventsLoop { - events_loop: ev, + LinuxEventLoop::new_x11().map(|ev| + EventLoop { + event_loop: ev, _marker: ::std::marker::PhantomData, } ) @@ -140,8 +140,8 @@ impl EventsLoopExt for EventsLoop { #[inline] fn new_wayland() -> Self { - EventsLoop { - events_loop: match LinuxEventsLoop::new_wayland() { + EventLoop { + event_loop: match LinuxEventLoop::new_wayland() { Ok(e) => e, Err(_) => panic!() // TODO: propagate }, @@ -151,7 +151,7 @@ impl EventsLoopExt for EventsLoop { #[inline] fn is_wayland(&self) -> bool { - self.events_loop.is_wayland() + self.event_loop.is_wayland() } #[inline] @@ -167,7 +167,7 @@ impl EventsLoopExt for EventsLoop { #[inline] #[doc(hidden)] fn get_xlib_xconnection(&self) -> Option> { - self.events_loop.x_connection().cloned() + self.event_loop.x_connection().cloned() } #[inline] @@ -189,7 +189,7 @@ impl EventsLoopExt for EventsLoop { } /// Additional methods on `Window` that are specific to Unix. -pub trait WindowExt { +pub trait WindowExtUnix { /// Returns the ID of the `Window` xlib object that is used by this window. /// /// Returns `None` if the window doesn't use xlib (if it uses wayland for example). @@ -251,7 +251,7 @@ pub trait WindowExt { fn is_ready(&self) -> bool; } -impl WindowExt for Window { +impl WindowExtUnix for Window { #[inline] fn get_xlib_window(&self) -> Option { match self.window { @@ -363,7 +363,7 @@ impl WindowExt for Window { } /// Additional methods on `WindowBuilder` that are specific to Unix. -pub trait WindowBuilderExt { +pub trait WindowBuilderExtUnix { fn with_x11_visual(self, visual_infos: *const T) -> WindowBuilder; fn with_x11_screen(self, screen_id: i32) -> WindowBuilder; @@ -388,7 +388,7 @@ pub trait WindowBuilderExt { fn with_app_id(self, app_id: String) -> WindowBuilder; } -impl WindowBuilderExt for WindowBuilder { +impl WindowBuilderExtUnix for WindowBuilder { #[inline] fn with_x11_visual(mut self, visual_infos: *const T) -> WindowBuilder { self.platform_specific.visual_infos = Some( @@ -446,13 +446,13 @@ impl WindowBuilderExt for WindowBuilder { } } -/// Additional methods on `MonitorId` that are specific to Linux. -pub trait MonitorIdExt { +/// Additional methods on `MonitorHandle` that are specific to Linux. +pub trait MonitorHandleExtUnix { /// Returns the inner identifier of the monitor. fn native_id(&self) -> u32; } -impl MonitorIdExt for MonitorId { +impl MonitorHandleExtUnix for MonitorHandle { #[inline] fn native_id(&self) -> u32 { self.inner.get_native_identifier() diff --git a/src/os/windows.rs b/src/platform/windows.rs similarity index 76% rename from src/os/windows.rs rename to src/platform/windows.rs index 62b16c7aca0..7a9de6880aa 100644 --- a/src/os/windows.rs +++ b/src/platform/windows.rs @@ -5,28 +5,31 @@ use std::os::raw::c_void; use libc; use winapi::shared::windef::HWND; -use {DeviceId, EventsLoop, Icon, MonitorId, Window, WindowBuilder}; -use platform::EventsLoop as WindowsEventsLoop; - -/// Additional methods on `EventsLoop` that are specific to Windows. -pub trait EventsLoopExt { +use event::DeviceId; +use monitor::MonitorHandle; +use event_loop::EventLoop; +use window::{Icon, Window, WindowBuilder}; +use platform_impl::EventLoop as WindowsEventLoop; + +/// Additional methods on `EventLoop` that are specific to Windows. +pub trait EventLoopExtWindows { /// By default, winit on Windows will attempt to enable process-wide DPI awareness. If that's - /// undesirable, you can create an `EventsLoop` using this function instead. + /// undesirable, you can create an `EventLoop` using this function instead. fn new_dpi_unaware() -> Self where Self: Sized; } -impl EventsLoopExt for EventsLoop { +impl EventLoopExtWindows for EventLoop { #[inline] fn new_dpi_unaware() -> Self { - EventsLoop { - events_loop: WindowsEventsLoop::with_dpi_awareness(false), + EventLoop { + event_loop: WindowsEventLoop::with_dpi_awareness(false), _marker: ::std::marker::PhantomData, } } } /// Additional methods on `Window` that are specific to Windows. -pub trait WindowExt { +pub trait WindowExtWindows { /// Returns the native handle that is used by this window. /// /// The pointer will become invalid when the native window was destroyed. @@ -36,7 +39,7 @@ pub trait WindowExt { fn set_taskbar_icon(&self, taskbar_icon: Option); } -impl WindowExt for Window { +impl WindowExtWindows for Window { #[inline] fn get_hwnd(&self) -> *mut libc::c_void { self.window.hwnd() as *mut _ @@ -49,7 +52,7 @@ impl WindowExt for Window { } /// Additional methods on `WindowBuilder` that are specific to Windows. -pub trait WindowBuilderExt { +pub trait WindowBuilderExtWindows { /// Sets a parent to the window to be created. fn with_parent_window(self, parent: HWND) -> WindowBuilder; @@ -60,7 +63,7 @@ pub trait WindowBuilderExt { fn with_no_redirection_bitmap(self, flag: bool) -> WindowBuilder; } -impl WindowBuilderExt for WindowBuilder { +impl WindowBuilderExtWindows for WindowBuilder { #[inline] fn with_parent_window(mut self, parent: HWND) -> WindowBuilder { self.platform_specific.parent = Some(parent); @@ -80,8 +83,8 @@ impl WindowBuilderExt for WindowBuilder { } } -/// Additional methods on `MonitorId` that are specific to Windows. -pub trait MonitorIdExt { +/// Additional methods on `MonitorHandle` that are specific to Windows. +pub trait MonitorHandleExtWindows { /// Returns the name of the monitor adapter specific to the Win32 API. fn native_id(&self) -> String; @@ -89,7 +92,7 @@ pub trait MonitorIdExt { fn hmonitor(&self) -> *mut c_void; } -impl MonitorIdExt for MonitorId { +impl MonitorHandleExtWindows for MonitorHandle { #[inline] fn native_id(&self) -> String { self.inner.get_native_identifier() @@ -102,14 +105,14 @@ impl MonitorIdExt for MonitorId { } /// Additional methods on `DeviceId` that are specific to Windows. -pub trait DeviceIdExt { +pub trait DeviceIdExtWindows { /// Returns an identifier that persistently refers to this specific device. /// /// Will return `None` if the device is no longer available. fn get_persistent_identifier(&self) -> Option; } -impl DeviceIdExt for DeviceId { +impl DeviceIdExtWindows for DeviceId { #[inline] fn get_persistent_identifier(&self) -> Option { self.0.get_persistent_identifier() diff --git a/src/platform/android/ffi.rs b/src/platform_impl/android/ffi.rs similarity index 100% rename from src/platform/android/ffi.rs rename to src/platform_impl/android/ffi.rs diff --git a/src/platform/android/mod.rs b/src/platform_impl/android/mod.rs similarity index 88% rename from src/platform/android/mod.rs rename to src/platform_impl/android/mod.rs index dad73801b8e..a6c692506de 100644 --- a/src/platform/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -24,36 +24,36 @@ use { }; use CreationError::OsError; use events::{Touch, TouchPhase}; -use window::MonitorId as RootMonitorId; +use window::MonitorHandle as RootMonitorHandle; -pub struct EventsLoop { +pub struct EventLoop { event_rx: Receiver, suspend_callback: RefCell ()>>>, } #[derive(Clone)] -pub struct EventsLoopProxy; +pub struct EventLoopProxy; -impl EventsLoop { - pub fn new() -> EventsLoop { +impl EventLoop { + pub fn new() -> EventLoop { let (tx, rx) = channel(); android_glue::add_sender(tx); - EventsLoop { + EventLoop { event_rx: rx, suspend_callback: Default::default(), } } #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { let mut rb = VecDeque::with_capacity(1); - rb.push_back(MonitorId); + rb.push_back(MonitorHandle); rb } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle } pub fn poll_events(&mut self, mut callback: F) @@ -62,7 +62,7 @@ impl EventsLoop { while let Ok(event) = self.event_rx.try_recv() { let e = match event{ android_glue::Event::EventMotion(motion) => { - let dpi_factor = MonitorId.get_hidpi_factor(); + let dpi_factor = MonitorHandle.get_hidpi_factor(); let location = LogicalPosition::from_physical( (motion.x as f64, motion.y as f64), dpi_factor, @@ -103,8 +103,8 @@ impl EventsLoop { if native_window.is_null() { None } else { - let dpi_factor = MonitorId.get_hidpi_factor(); - let physical_size = MonitorId.get_dimensions(); + let dpi_factor = MonitorHandle.get_hidpi_factor(); + let physical_size = MonitorHandle.get_dimensions(); let size = LogicalSize::from_physical(physical_size, dpi_factor); Some(Event::WindowEvent { window_id: RootWindowId(WindowId), @@ -116,7 +116,7 @@ impl EventsLoop { // The activity needs to be redrawn. Some(Event::WindowEvent { window_id: RootWindowId(WindowId), - event: WindowEvent::Refresh, + event: WindowEvent::Redraw, }) } android_glue::Event::Wake => { @@ -155,13 +155,13 @@ impl EventsLoop { } } - pub fn create_proxy(&self) -> EventsLoopProxy { - EventsLoopProxy + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy } } -impl EventsLoopProxy { - pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> { +impl EventLoopProxy { + pub fn wakeup(&self) -> Result<(), ::EventLoopClosed> { android_glue::wake_event_loop(); Ok(()) } @@ -190,19 +190,19 @@ pub struct Window { } #[derive(Clone)] -pub struct MonitorId; +pub struct MonitorHandle; -impl fmt::Debug for MonitorId { +impl fmt::Debug for MonitorHandle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[derive(Debug)] - struct MonitorId { + struct MonitorHandle { name: Option, dimensions: PhysicalSize, position: PhysicalPosition, hidpi_factor: f64, } - let monitor_id_proxy = MonitorId { + let monitor_id_proxy = MonitorHandle { name: self.get_name(), dimensions: self.get_dimensions(), position: self.get_position(), @@ -213,7 +213,7 @@ impl fmt::Debug for MonitorId { } } -impl MonitorId { +impl MonitorHandle { #[inline] pub fn get_name(&self) -> Option { Some("Primary".to_string()) @@ -248,7 +248,7 @@ pub struct PlatformSpecificWindowBuilderAttributes; pub struct PlatformSpecificHeadlessBuilderAttributes; impl Window { - pub fn new(_: &EventsLoop, win_attribs: WindowAttributes, + pub fn new(_: &EventLoop, win_attribs: WindowAttributes, _: PlatformSpecificWindowBuilderAttributes) -> Result { @@ -369,7 +369,7 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, _monitor: Option) { + pub fn set_fullscreen(&self, _monitor: Option) { // N/A // Android has single screen maximized apps so nothing to do } @@ -395,20 +395,20 @@ impl Window { } #[inline] - pub fn get_current_monitor(&self) -> RootMonitorId { - RootMonitorId { inner: MonitorId } + pub fn get_current_monitor(&self) -> RootMonitorHandle { + RootMonitorHandle { inner: MonitorHandle } } #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { let mut rb = VecDeque::with_capacity(1); - rb.push_back(MonitorId); + rb.push_back(MonitorHandle); rb } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle } #[inline] diff --git a/src/platform/emscripten/ffi.rs b/src/platform_impl/emscripten/ffi.rs similarity index 100% rename from src/platform/emscripten/ffi.rs rename to src/platform_impl/emscripten/ffi.rs diff --git a/src/platform/emscripten/mod.rs b/src/platform_impl/emscripten/mod.rs similarity index 96% rename from src/platform/emscripten/mod.rs rename to src/platform_impl/emscripten/mod.rs index 3a5feb61d62..571d6b45ea1 100644 --- a/src/platform/emscripten/mod.rs +++ b/src/platform_impl/emscripten/mod.rs @@ -10,7 +10,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Mutex, Arc}; use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; -use window::MonitorId as RootMonitorId; +use window::MonitorHandle as RootMonitorHandle; const DOCUMENT_NAME: &'static str = "#document\0"; @@ -37,9 +37,9 @@ impl DeviceId { pub struct PlatformSpecificHeadlessBuilderAttributes; #[derive(Debug, Clone)] -pub struct MonitorId; +pub struct MonitorHandle; -impl MonitorId { +impl MonitorHandle { #[inline] pub fn get_name(&self) -> Option { Some("Canvas".to_owned()) @@ -81,22 +81,22 @@ pub fn set_main_loop_callback(callback : F) where F : FnMut() { } #[derive(Clone)] -pub struct EventsLoopProxy; +pub struct EventLoopProxy; -impl EventsLoopProxy { - pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> { +impl EventLoopProxy { + pub fn wakeup(&self) -> Result<(), ::EventLoopClosed> { unimplemented!() } } -pub struct EventsLoop { +pub struct EventLoop { window: Mutex>>, interrupted: AtomicBool, } -impl EventsLoop { - pub fn new() -> EventsLoop { - EventsLoop { +impl EventLoop { + pub fn new() -> EventLoop { + EventLoop { window: Mutex::new(None), interrupted: AtomicBool::new(false), } @@ -108,20 +108,20 @@ impl EventsLoop { } #[inline] - pub fn create_proxy(&self) -> EventsLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { unimplemented!() } #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { let mut list = VecDeque::with_capacity(1); - list.push_back(MonitorId); + list.push_back(MonitorHandle); list } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle } pub fn poll_events(&self, mut callback: F) @@ -378,11 +378,11 @@ fn em_try(res: ffi::EMSCRIPTEN_RESULT) -> Result<(), String> { } impl Window { - pub fn new(events_loop: &EventsLoop, attribs: ::WindowAttributes, + pub fn new(event_loop: &EventLoop, attribs: ::WindowAttributes, _pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result { - if events_loop.window.lock().unwrap().is_some() { + if event_loop.window.lock().unwrap().is_some() { return Err(::CreationError::OsError("Cannot create another window".to_owned())); } @@ -431,7 +431,7 @@ impl Window { window.set_inner_size(size); } - *events_loop.window.lock().unwrap() = Some(window.window.clone()); + *event_loop.window.lock().unwrap() = Some(window.window.clone()); Ok(window) } @@ -581,7 +581,7 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, _monitor: Option<::MonitorId>) { + pub fn set_fullscreen(&self, _monitor: Option<::MonitorHandle>) { // iOS has single screen maximized apps so nothing to do } @@ -606,28 +606,28 @@ impl Window { } #[inline] - pub fn get_current_monitor(&self) -> RootMonitorId { - RootMonitorId { inner: MonitorId } + pub fn get_current_monitor(&self) -> RootMonitorHandle { + RootMonitorHandle { inner: MonitorHandle } } #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { let mut list = VecDeque::with_capacity(1); - list.push_back(MonitorId); + list.push_back(MonitorHandle); list } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle } } impl Drop for Window { fn drop(&mut self) { - // Delete window from events_loop + // Delete window from event_loop // TODO: ? - /*if let Some(ev) = self.events_loop.upgrade() { + /*if let Some(ev) = self.event_loop.upgrade() { let _ = ev.window.lock().unwrap().take().unwrap(); }*/ diff --git a/src/platform/ios/ffi.rs b/src/platform_impl/ios/ffi.rs similarity index 100% rename from src/platform/ios/ffi.rs rename to src/platform_impl/ios/ffi.rs diff --git a/src/platform/ios/mod.rs b/src/platform_impl/ios/mod.rs similarity index 93% rename from src/platform/ios/mod.rs rename to src/platform_impl/ios/mod.rs index 6830ace4a9a..da41d15d286 100644 --- a/src/platform/ios/mod.rs +++ b/src/platform_impl/ios/mod.rs @@ -82,7 +82,7 @@ use { WindowId as RootEventId, }; use events::{Touch, TouchPhase}; -use window::MonitorId as RootMonitorId; +use window::MonitorHandle as RootMonitorHandle; mod ffi; use self::ffi::{ @@ -145,19 +145,19 @@ impl Drop for DelegateState { } #[derive(Clone)] -pub struct MonitorId; +pub struct MonitorHandle; -impl fmt::Debug for MonitorId { +impl fmt::Debug for MonitorHandle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[derive(Debug)] - struct MonitorId { + struct MonitorHandle { name: Option, dimensions: PhysicalSize, position: PhysicalPosition, hidpi_factor: f64, } - let monitor_id_proxy = MonitorId { + let monitor_id_proxy = MonitorHandle { name: self.get_name(), dimensions: self.get_dimensions(), position: self.get_position(), @@ -168,7 +168,7 @@ impl fmt::Debug for MonitorId { } } -impl MonitorId { +impl MonitorHandle { #[inline] pub fn get_uiscreen(&self) -> id { let class = class!(UIScreen); @@ -199,33 +199,33 @@ impl MonitorId { } } -pub struct EventsLoop { +pub struct EventLoop { events_queue: Arc>>, } #[derive(Clone)] -pub struct EventsLoopProxy; +pub struct EventLoopProxy; -impl EventsLoop { - pub fn new() -> EventsLoop { +impl EventLoop { + pub fn new() -> EventLoop { unsafe { if !msg_send![class!(NSThread), isMainThread] { - panic!("`EventsLoop` can only be created on the main thread on iOS"); + panic!("`EventLoop` can only be created on the main thread on iOS"); } } - EventsLoop { events_queue: Default::default() } + EventLoop { events_queue: Default::default() } } #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { let mut rb = VecDeque::with_capacity(1); - rb.push_back(MonitorId); + rb.push_back(MonitorHandle); rb } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle } pub fn poll_events(&mut self, mut callback: F) @@ -238,7 +238,7 @@ impl EventsLoop { unsafe { // jump hack, so we won't quit on willTerminate event before processing it - assert!(JMPBUF.is_some(), "`EventsLoop::poll_events` must be called after window creation on iOS"); + assert!(JMPBUF.is_some(), "`EventLoop::poll_events` must be called after window creation on iOS"); if setjmp(mem::transmute_copy(&mut JMPBUF)) != 0 { if let Some(event) = self.events_queue.borrow_mut().pop_front() { callback(event); @@ -276,13 +276,13 @@ impl EventsLoop { } } - pub fn create_proxy(&self) -> EventsLoopProxy { - EventsLoopProxy + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy } } -impl EventsLoopProxy { - pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> { +impl EventLoopProxy { + pub fn wakeup(&self) -> Result<(), ::EventLoopClosed> { unimplemented!() } } @@ -322,7 +322,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes { // so to be consistent with other platforms we have to change that. impl Window { pub fn new( - ev: &EventsLoop, + ev: &EventLoop, _attributes: WindowAttributes, pl_attributes: PlatformSpecificWindowBuilderAttributes, ) -> Result { @@ -342,7 +342,7 @@ impl Window { (&mut *delegate).set_ivar("eventsQueue", mem::transmute::<_, *mut c_void>(events_queue)); // easiest? way to get access to PlatformSpecificWindowBuilderAttributes to configure the view - let rect: CGRect = msg_send![MonitorId.get_uiscreen(), bounds]; + let rect: CGRect = msg_send![MonitorHandle.get_uiscreen(), bounds]; let uiview_class = class!(UIView); let root_view_class = pl_attributes.root_view_class; @@ -474,7 +474,7 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, _monitor: Option) { + pub fn set_fullscreen(&self, _monitor: Option) { // N/A // iOS has single screen maximized apps so nothing to do } @@ -500,20 +500,20 @@ impl Window { } #[inline] - pub fn get_current_monitor(&self) -> RootMonitorId { - RootMonitorId { inner: MonitorId } + pub fn get_current_monitor(&self) -> RootMonitorHandle { + RootMonitorHandle { inner: MonitorHandle } } #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { let mut rb = VecDeque::with_capacity(1); - rb.push_back(MonitorId); + rb.push_back(MonitorHandle); rb } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle } #[inline] diff --git a/src/platform/linux/dlopen.rs b/src/platform_impl/linux/dlopen.rs similarity index 100% rename from src/platform/linux/dlopen.rs rename to src/platform_impl/linux/dlopen.rs diff --git a/src/platform/linux/mod.rs b/src/platform_impl/linux/mod.rs similarity index 71% rename from src/platform/linux/mod.rs rename to src/platform_impl/linux/mod.rs index 6c4dd4330da..100322aac2f 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -11,14 +11,14 @@ use sctk::reexports::client::ConnectError; use { CreationError, - EventsLoopClosed, + EventLoopClosed, Icon, MouseCursor, ControlFlow, WindowAttributes, }; use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; -use window::MonitorId as RootMonitorId; +use window::MonitorHandle as RootMonitorHandle; use self::x11::{XConnection, XError}; use self::x11::ffi::XVisualInfo; pub use self::x11::XNotSupported; @@ -89,55 +89,55 @@ impl DeviceId { } #[derive(Debug, Clone)] -pub enum MonitorId { - X(x11::MonitorId), - Wayland(wayland::MonitorId), - Gbm(gbm::MonitorId), +pub enum MonitorHandle { + X(x11::MonitorHandle), + Wayland(wayland::MonitorHandle), + Gbm(gbm::MonitorHandle), } -impl MonitorId { +impl MonitorHandle { #[inline] pub fn get_name(&self) -> Option { match self { - &MonitorId::X(ref m) => m.get_name(), - &MonitorId::Wayland(ref m) => m.get_name(), - &MonitorId::Gbm(ref m) => m.get_name(), + &MonitorHandle::X(ref m) => m.get_name(), + &MonitorHandle::Wayland(ref m) => m.get_name(), + &MonitorHandle::Gbm(ref m) => m.get_name(), } } #[inline] pub fn get_native_identifier(&self) -> u32 { match self { - &MonitorId::X(ref m) => m.get_native_identifier(), - &MonitorId::Wayland(ref m) => m.get_native_identifier(), - &MonitorId::Gbm(ref m) => m.get_native_identifier(), + &MonitorHandle::X(ref m) => m.get_native_identifier(), + &MonitorHandle::Wayland(ref m) => m.get_native_identifier(), + &MonitorHandle::Gbm(ref m) => m.get_native_identifier(), } } #[inline] pub fn get_dimensions(&self) -> PhysicalSize { match self { - &MonitorId::X(ref m) => m.get_dimensions(), - &MonitorId::Wayland(ref m) => m.get_dimensions(), - &MonitorId::Gbm(ref m) => m.get_dimensions(), + &MonitorHandle::X(ref m) => m.get_dimensions(), + &MonitorHandle::Wayland(ref m) => m.get_dimensions(), + &MonitorHandle::Gbm(ref m) => m.get_dimensions(), } } #[inline] pub fn get_position(&self) -> PhysicalPosition { match self { - &MonitorId::X(ref m) => m.get_position(), - &MonitorId::Wayland(ref m) => m.get_position(), - &MonitorId::Gbm(ref m) => m.get_position(), + &MonitorHandle::X(ref m) => m.get_position(), + &MonitorHandle::Wayland(ref m) => m.get_position(), + &MonitorHandle::Gbm(ref m) => m.get_position(), } } #[inline] pub fn get_hidpi_factor(&self) -> f64 { match self { - &MonitorId::X(ref m) => m.get_hidpi_factor(), - &MonitorId::Wayland(ref m) => m.get_hidpi_factor() as f64, - &MonitorId::Gbm(ref m) => m.get_hidpi_factor() as f64, + &MonitorHandle::X(ref m) => m.get_hidpi_factor(), + &MonitorHandle::Wayland(ref m) => m.get_hidpi_factor() as f64, + &MonitorHandle::Gbm(ref m) => m.get_hidpi_factor() as f64, } } } @@ -145,18 +145,18 @@ impl MonitorId { impl Window { #[inline] pub fn new( - events_loop: &EventsLoop, + event_loop: &EventLoop, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result { - match *events_loop { - EventsLoop::Wayland(ref events_loop) => { - wayland::Window::new(events_loop, attribs, pl_attribs).map(Window::Wayland) + match *event_loop { + EventLoop::Wayland(ref event_loop) => { + wayland::Window::new(event_loop, attribs, pl_attribs).map(Window::Wayland) }, - EventsLoop::X(ref events_loop) => { - x11::Window::new(events_loop, attribs, pl_attribs).map(Window::X) + EventLoop::X(ref event_loop) => { + x11::Window::new(event_loop, attribs, pl_attribs).map(Window::X) }, - EventsLoop::Gbm(ref events_loop) => { + EventLoop::Gbm(ref events_loop) => { gbm::Window::new(events_loop, attribs, pl_attribs).map(Window::Gbm) }, } @@ -334,7 +334,7 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, monitor: Option) { match self { &Window::X(ref w) => w.set_fullscreen(monitor), &Window::Wayland(ref w) => w.set_fullscreen(monitor), @@ -379,38 +379,38 @@ impl Window { } #[inline] - pub fn get_current_monitor(&self) -> RootMonitorId { + pub fn get_current_monitor(&self) -> RootMonitorHandle { match self { - &Window::X(ref window) => RootMonitorId { inner: MonitorId::X(window.get_current_monitor()) }, - &Window::Wayland(ref window) => RootMonitorId { inner: MonitorId::Wayland(window.get_current_monitor()) }, - &Window::Gbm(ref window) => RootMonitorId { inner: MonitorId::Gbm(window.get_current_monitor()) }, + &Window::X(ref window) => RootMonitorHandle { inner: MonitorHandle::X(window.get_current_monitor()) }, + &Window::Wayland(ref window) => RootMonitorHandle { inner: MonitorHandle::Wayland(window.get_current_monitor()) }, + &Window::Gbm(ref window) => RootMonitorHandle { inner: MonitorHandle::Gbm(window.get_current_monitor()) }, } } #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { match self { &Window::X(ref window) => window.get_available_monitors() .into_iter() - .map(MonitorId::X) + .map(MonitorHandle::X) .collect(), &Window::Wayland(ref window) => window.get_available_monitors() .into_iter() - .map(MonitorId::Wayland) + .map(MonitorHandle::Wayland) .collect(), &Window::Gbm(ref window) => window.get_available_monitors() .into_iter() - .map(MonitorId::Gbm) + .map(MonitorHandle::Gbm) .collect(), } } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { + pub fn get_primary_monitor(&self) -> MonitorHandle { match self { - &Window::X(ref window) => MonitorId::X(window.get_primary_monitor()), - &Window::Wayland(ref window) => MonitorId::Wayland(window.get_primary_monitor()), - &Window::Gbm(ref window) => MonitorId::Gbm(window.get_primary_monitor()), + &Window::X(ref window) => MonitorHandle::X(window.get_primary_monitor()), + &Window::Wayland(ref window) => MonitorHandle::Wayland(window.get_primary_monitor()), + &Window::Gbm(ref window) => MonitorHandle::Gbm(window.get_primary_monitor()), } } } @@ -445,33 +445,33 @@ unsafe extern "C" fn x_error_callback( 0 } -pub enum EventsLoop { - Wayland(wayland::EventsLoop), - X(x11::EventsLoop), - Gbm(gbm::EventsLoop), +pub enum EventLoop { + Wayland(wayland::EventLoop), + X(x11::EventLoop), + Gbm(gbm::EventLoop), } #[derive(Clone)] -pub enum EventsLoopProxy { - X(x11::EventsLoopProxy), - Wayland(wayland::EventsLoopProxy), - Gbm(gbm::EventsLoopProxy), +pub enum EventLoopProxy { + X(x11::EventLoopProxy), + Wayland(wayland::EventLoopProxy), + Gbm(gbm::EventLoopProxy), } -impl EventsLoop { - pub fn new() -> EventsLoop { +impl EventLoop { + pub fn new() -> EventLoop { if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) { match env_var.as_str() { "x11" => { // TODO: propagate - return EventsLoop::new_x11().expect("Failed to initialize X11 backend"); + return EventLoop::new_x11().expect("Failed to initialize X11 backend"); }, "wayland" => { - return EventsLoop::new_wayland() + return EventLoop::new_wayland() .expect("Failed to initialize Wayland backend"); }, "gbm" => { - return EventsLoop::new_gbm() + return EventLoop::new_gbm() .expect("Failed to initialize GLES2 backend"); }, _ => panic!( @@ -481,17 +481,17 @@ impl EventsLoop { } } - let wayland_err = match EventsLoop::new_wayland() { + let wayland_err = match EventLoop::new_wayland() { Ok(event_loop) => return event_loop, Err(err) => err, }; - let x11_err = match EventsLoop::new_x11() { + let x11_err = match EventLoop::new_x11() { Ok(event_loop) => return event_loop, Err(err) => err, }; - let gbm_err = match EventsLoop::new_gbm() { + let gbm_err = match EventLoop::new_gbm() { Ok(event_loop) => return event_loop, Err(err) => err, }; @@ -505,62 +505,62 @@ impl EventsLoop { panic!(err_string); } - pub fn new_wayland() -> Result { - wayland::EventsLoop::new() - .map(EventsLoop::Wayland) + pub fn new_wayland() -> Result { + wayland::EventLoop::new() + .map(EventLoop::Wayland) } - pub fn new_gbm() -> Result { - gbm::EventsLoop::new() - .map(EventsLoop::Gbm) + pub fn new_gbm() -> Result { + gbm::EventLoop::new() + .map(EventLoop::Gbm) } - pub fn new_x11() -> Result { + pub fn new_x11() -> Result { X11_BACKEND .lock() .as_ref() .map(Arc::clone) - .map(x11::EventsLoop::new) - .map(EventsLoop::X) + .map(x11::EventLoop::new) + .map(EventLoop::X) .map_err(|err| err.clone()) } #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { match *self { - EventsLoop::Wayland(ref evlp) => evlp + EventLoop::Wayland(ref evlp) => evlp .get_available_monitors() .into_iter() - .map(MonitorId::Wayland) + .map(MonitorHandle::Wayland) .collect(), - EventsLoop::X(ref evlp) => evlp + EventLoop::X(ref evlp) => evlp .x_connection() .get_available_monitors() .into_iter() - .map(MonitorId::X) + .map(MonitorHandle::X) .collect(), - EventsLoop::Gbm(ref evlp) => evlp + EventLoop::Gbm(ref evlp) => evlp .get_available_monitors() .into_iter() - .map(MonitorId::Gbm) + .map(MonitorHandle::Gbm) .collect(), } } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { + pub fn get_primary_monitor(&self) -> MonitorHandle { match *self { - EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()), - EventsLoop::X(ref evlp) => MonitorId::X(evlp.x_connection().get_primary_monitor()), - EventsLoop::Gbm(ref evlp) => MonitorId::Gbm(evlp.get_primary_monitor()), + EventLoop::Wayland(ref evlp) => MonitorHandle::Wayland(evlp.get_primary_monitor()), + EventLoop::X(ref evlp) => MonitorHandle::X(evlp.x_connection().get_primary_monitor()), + EventLoop::Gbm(ref evlp) => MonitorHandle::Gbm(evlp.get_primary_monitor()), } } - pub fn create_proxy(&self) -> EventsLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { match *self { - EventsLoop::Wayland(ref evlp) => EventsLoopProxy::Wayland(evlp.create_proxy()), - EventsLoop::X(ref evlp) => EventsLoopProxy::X(evlp.create_proxy()), - EventsLoop::Gbm(ref evlp) => EventsLoopProxy::Gbm(evlp.create_proxy()), + EventLoop::Wayland(ref evlp) => EventLoopProxy::Wayland(evlp.create_proxy()), + EventLoop::X(ref evlp) => EventLoopProxy::X(evlp.create_proxy()), + EventLoop::Gbm(ref evlp) => EventLoopProxy::Gbm(evlp.create_proxy()), } } @@ -568,9 +568,9 @@ impl EventsLoop { where F: FnMut(::Event) { match *self { - EventsLoop::Wayland(ref mut evlp) => evlp.poll_events(callback), - EventsLoop::X(ref mut evlp) => evlp.poll_events(callback), - EventsLoop::Gbm(ref mut evlp) => evlp.poll_events(callback), + EventLoop::Wayland(ref mut evlp) => evlp.poll_events(callback), + EventLoop::X(ref mut evlp) => evlp.poll_events(callback), + EventLoop::Gbm(ref mut evlp) => evlp.poll_events(callback), } } @@ -578,46 +578,46 @@ impl EventsLoop { where F: FnMut(::Event) -> ControlFlow { match *self { - EventsLoop::Wayland(ref mut evlp) => evlp.run_forever(callback), - EventsLoop::X(ref mut evlp) => evlp.run_forever(callback), - EventsLoop::Gbm(ref mut evlp) => evlp.run_forever(callback), + EventLoop::Wayland(ref mut evlp) => evlp.run_forever(callback), + EventLoop::X(ref mut evlp) => evlp.run_forever(callback), + EventLoop::Gbm(ref mut evlp) => evlp.run_forever(callback), } } #[inline] pub fn is_wayland(&self) -> bool { match *self { - EventsLoop::Wayland(_) => true, - EventsLoop::X(_) => false, - EventsLoop::Gbm(_) => false, + EventLoop::Wayland(_) => true, + EventLoop::X(_) => false, + EventLoop::Gbm(_) => false, } } #[inline] pub fn is_gbm(&self) -> bool { match *self { - EventsLoop::Wayland(_) => false, - EventsLoop::X(_) => false, - EventsLoop::Gbm(_) => true, + EventLoop::Wayland(_) => false, + EventLoop::X(_) => false, + EventLoop::Gbm(_) => true, } } #[inline] pub fn x_connection(&self) -> Option<&Arc> { match *self { - EventsLoop::Wayland(_) => None, - EventsLoop::X(ref ev) => Some(ev.x_connection()), - EventsLoop::Gbm(_) => None, + EventLoop::Wayland(_) => None, + EventLoop::X(ref ev) => Some(ev.x_connection()), + EventLoop::Gbm(_) => None, } } } -impl EventsLoopProxy { - pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { +impl EventLoopProxy { + pub fn wakeup(&self) -> Result<(), EventLoopClosed> { match *self { - EventsLoopProxy::Wayland(ref proxy) => proxy.wakeup(), - EventsLoopProxy::X(ref proxy) => proxy.wakeup(), - EventsLoopProxy::Gbm(ref proxy) => proxy.wakeup(), + EventLoopProxy::Wayland(ref proxy) => proxy.wakeup(), + EventLoopProxy::X(ref proxy) => proxy.wakeup(), + EventLoopProxy::Gbm(ref proxy) => proxy.wakeup(), } } } diff --git a/src/platform/linux/wayland/event_loop.rs b/src/platform_impl/linux/wayland/event_loop.rs similarity index 78% rename from src/platform/linux/wayland/event_loop.rs rename to src/platform_impl/linux/wayland/event_loop.rs index 82275e634e3..3835688d294 100644 --- a/src/platform/linux/wayland/event_loop.rs +++ b/src/platform_impl/linux/wayland/event_loop.rs @@ -4,7 +4,7 @@ use std::fmt; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; -use {ControlFlow, EventsLoopClosed, PhysicalPosition, PhysicalSize}; +use {ControlFlow, EventLoopClosed, PhysicalPosition, PhysicalSize}; use super::window::WindowStore; use super::WindowId; @@ -21,13 +21,13 @@ use sctk::reexports::client::protocol::wl_surface::RequestsTrait; use ModifiersState; -pub struct EventsLoopSink { +pub struct EventLoopSink { buffer: VecDeque<::Event>, } -impl EventsLoopSink { - pub fn new() -> EventsLoopSink { - EventsLoopSink { +impl EventLoopSink { + pub fn new() -> EventLoopSink { + EventLoopSink { buffer: VecDeque::new(), } } @@ -54,11 +54,11 @@ impl EventsLoopSink { } } -pub struct EventsLoop { +pub struct EventLoop { // The Event Queue pub evq: RefCell, // our sink, shared with some handlers, buffering the events - sink: Arc>, + sink: Arc>, // Whether or not there is a pending `Awakened` event to be emitted. pending_wakeup: Arc, // The window store @@ -73,43 +73,43 @@ pub struct EventsLoop { pub seats: Arc)>>>, } -// A handle that can be sent across threads and used to wake up the `EventsLoop`. +// A handle that can be sent across threads and used to wake up the `EventLoop`. // -// We should only try and wake up the `EventsLoop` if it still exists, so we hold Weak ptrs. +// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs. #[derive(Clone)] -pub struct EventsLoopProxy { +pub struct EventLoopProxy { display: Weak, pending_wakeup: Weak, } -impl EventsLoopProxy { - // Causes the `EventsLoop` to stop blocking on `run_forever` and emit an `Awakened` event. +impl EventLoopProxy { + // Causes the `EventLoop` to stop blocking on `run_forever` and emit an `Awakened` event. // - // Returns `Err` if the associated `EventsLoop` no longer exists. - pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + // Returns `Err` if the associated `EventLoop` no longer exists. + pub fn wakeup(&self) -> Result<(), EventLoopClosed> { let display = self.display.upgrade(); let wakeup = self.pending_wakeup.upgrade(); match (display, wakeup) { (Some(display), Some(wakeup)) => { - // Update the `EventsLoop`'s `pending_wakeup` flag. + // Update the `EventLoop`'s `pending_wakeup` flag. wakeup.store(true, Ordering::Relaxed); - // Cause the `EventsLoop` to break from `dispatch` if it is currently blocked. + // Cause the `EventLoop` to break from `dispatch` if it is currently blocked. let _ = display.sync(|callback| callback.implement(|_, _| {}, ())); - display.flush().map_err(|_| EventsLoopClosed)?; + display.flush().map_err(|_| EventLoopClosed)?; Ok(()) } - _ => Err(EventsLoopClosed), + _ => Err(EventLoopClosed), } } } -impl EventsLoop { - pub fn new() -> Result { +impl EventLoop { + pub fn new() -> Result { let (display, mut event_queue) = Display::connect_to_env()?; let display = Arc::new(display); let pending_wakeup = Arc::new(AtomicBool::new(false)); - let sink = Arc::new(Mutex::new(EventsLoopSink::new())); + let sink = Arc::new(Mutex::new(EventLoopSink::new())); let store = Arc::new(Mutex::new(WindowStore::new())); let seats = Arc::new(Mutex::new(Vec::new())); @@ -142,7 +142,7 @@ impl EventsLoop { }, ).unwrap(); - Ok(EventsLoop { + Ok(EventLoop { display, evq: RefCell::new(event_queue), sink, @@ -154,8 +154,8 @@ impl EventsLoop { }) } - pub fn create_proxy(&self) -> EventsLoopProxy { - EventsLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { display: Arc::downgrade(&self.display), pending_wakeup: Arc::downgrade(&self.pending_wakeup), } @@ -222,11 +222,11 @@ impl EventsLoop { } } - pub fn get_primary_monitor(&self) -> MonitorId { + pub fn get_primary_monitor(&self) -> MonitorHandle { get_primary_monitor(&self.env.outputs) } - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { get_available_monitors(&self.env.outputs) } @@ -236,10 +236,10 @@ impl EventsLoop { } /* - * Private EventsLoop Internals + * Private EventLoop Internals */ -impl EventsLoop { +impl EventLoop { fn post_dispatch_triggers(&mut self) { let mut sink = self.sink.lock().unwrap(); // process a possible pending wakeup call @@ -279,7 +279,7 @@ impl EventsLoop { sink.send_event(::WindowEvent::HiDpiFactorChanged(dpi as f64), wid); } if refresh { - sink.send_event(::WindowEvent::Refresh, wid); + sink.send_event(::WindowEvent::Redraw, wid); } if closed { sink.send_event(::WindowEvent::CloseRequested, wid); @@ -294,10 +294,10 @@ impl EventsLoop { */ struct SeatManager { - sink: Arc>, + sink: Arc>, store: Arc>, seats: Arc)>>>, - events_loop_proxy: EventsLoopProxy, + event_loop_proxy: EventLoopProxy, } impl SeatManager { @@ -327,24 +327,80 @@ impl SeatManager { fn remove_seat(&mut self, id: u32) { use self::wl_seat::RequestsTrait as SeatRequests; +<<<<<<< HEAD:src/platform_impl/linux/wayland/event_loop.rs + match evt { + GlobalEvent::New { + id, + ref interface, + version, + } if interface == "wl_seat" => + { + use std::cmp::min; + + let mut seat_data = SeatData { + sink: self.sink.clone(), + store: self.store.clone(), + pointer: None, + keyboard: None, + touch: None, + events_loop_proxy: self.events_loop_proxy.clone(), + modifiers_tracker: Arc::new(Mutex::new(ModifiersState::default())), + }; + let seat = registry +<<<<<<< HEAD + .bind(min(version, 5), id, move |seat| { + seat.implement(move |event, seat| { + seat_data.receive(event, seat) + }, ()) + }) + .unwrap(); +======= + .bind::(min(version, 5), id) + .unwrap() + .implement(SeatData { + sink: self.sink.clone(), + store: self.store.clone(), + pointer: None, + keyboard: None, + touch: None, + event_loop_proxy: self.event_loop_proxy.clone(), + }); +>>>>>>> Change instances of "events_loop" to "event_loop" + self.store.lock().unwrap().new_seat(&seat); + self.seats.lock().unwrap().push((id, seat)); + } + GlobalEvent::Removed { id, ref interface } if interface == "wl_seat" => { + let mut seats = self.seats.lock().unwrap(); + if let Some(idx) = seats.iter().position(|&(i, _)| i == id) { + let (_, seat) = seats.swap_remove(idx); + if seat.version() >= 5 { + seat.release(); + } + } +======= let mut seats = self.seats.lock().unwrap(); if let Some(idx) = seats.iter().position(|&(i, _)| i == id) { let (_, seat) = seats.swap_remove(idx); if seat.version() >= 5 { seat.release(); +>>>>>>> master:src/platform/linux/wayland/event_loop.rs } } } } struct SeatData { - sink: Arc>, + sink: Arc>, store: Arc>, pointer: Option>, keyboard: Option>, touch: Option>, +<<<<<<< HEAD events_loop_proxy: EventsLoopProxy, modifiers_tracker: Arc>, +======= + event_loop_proxy: EventLoopProxy, +>>>>>>> Change instances of "events_loop" to "event_loop" } impl SeatData { @@ -375,8 +431,12 @@ impl SeatData { self.keyboard = Some(super::keyboard::init_keyboard( &seat, self.sink.clone(), +<<<<<<< HEAD self.events_loop_proxy.clone(), self.modifiers_tracker.clone(), +======= + self.event_loop_proxy.clone(), +>>>>>>> Change instances of "events_loop" to "event_loop" )) } // destroy keyboard if applicable @@ -437,24 +497,24 @@ impl Drop for SeatData { * Monitor stuff */ -pub struct MonitorId { +pub struct MonitorHandle { pub(crate) proxy: Proxy, pub(crate) mgr: OutputMgr, } -impl Clone for MonitorId { - fn clone(&self) -> MonitorId { - MonitorId { +impl Clone for MonitorHandle { + fn clone(&self) -> MonitorHandle { + MonitorHandle { proxy: self.proxy.clone(), mgr: self.mgr.clone(), } } } -impl fmt::Debug for MonitorId { +impl fmt::Debug for MonitorHandle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[derive(Debug)] - struct MonitorId { + struct MonitorHandle { name: Option, native_identifier: u32, dimensions: PhysicalSize, @@ -462,7 +522,7 @@ impl fmt::Debug for MonitorId { hidpi_factor: i32, } - let monitor_id_proxy = MonitorId { + let monitor_id_proxy = MonitorHandle { name: self.get_name(), native_identifier: self.get_native_identifier(), dimensions: self.get_dimensions(), @@ -474,7 +534,7 @@ impl fmt::Debug for MonitorId { } } -impl MonitorId { +impl MonitorHandle { pub fn get_name(&self) -> Option { self.mgr.with_info(&self.proxy, |_, info| { format!("{} ({})", info.model, info.make) @@ -513,10 +573,10 @@ impl MonitorId { } } -pub fn get_primary_monitor(outputs: &OutputMgr) -> MonitorId { +pub fn get_primary_monitor(outputs: &OutputMgr) -> MonitorHandle { outputs.with_all(|list| { if let Some(&(_, ref proxy, _)) = list.first() { - MonitorId { + MonitorHandle { proxy: proxy.clone(), mgr: outputs.clone(), } @@ -526,10 +586,10 @@ pub fn get_primary_monitor(outputs: &OutputMgr) -> MonitorId { }) } -pub fn get_available_monitors(outputs: &OutputMgr) -> VecDeque { +pub fn get_available_monitors(outputs: &OutputMgr) -> VecDeque { outputs.with_all(|list| { list.iter() - .map(|&(_, ref proxy, _)| MonitorId { + .map(|&(_, ref proxy, _)| MonitorHandle { proxy: proxy.clone(), mgr: outputs.clone(), }) diff --git a/src/platform/linux/wayland/keyboard.rs b/src/platform_impl/linux/wayland/keyboard.rs similarity index 98% rename from src/platform/linux/wayland/keyboard.rs rename to src/platform_impl/linux/wayland/keyboard.rs index a20724e0f9b..74dddfa70e6 100644 --- a/src/platform/linux/wayland/keyboard.rs +++ b/src/platform_impl/linux/wayland/keyboard.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use super::{make_wid, DeviceId, EventsLoopProxy, EventsLoopSink}; +use super::{make_wid, DeviceId, EventLoopProxy, EventLoopSink}; use sctk::keyboard::{ self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind, }; @@ -13,8 +13,8 @@ use {ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}; pub fn init_keyboard( seat: &Proxy, - sink: Arc>, - events_loop_proxy: EventsLoopProxy, + sink: Arc>, + event_loop_proxy: EventLoopProxy, modifiers_tracker: Arc>, ) -> Proxy { // { variables to be captured by the closures @@ -108,7 +108,7 @@ pub fn init_keyboard( guard.send_event(WindowEvent::ReceivedCharacter(chr), wid); } } - events_loop_proxy.wakeup().unwrap(); + event_loop_proxy.wakeup().unwrap(); } }, ); diff --git a/src/platform/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs similarity index 90% rename from src/platform/linux/wayland/mod.rs rename to src/platform_impl/linux/wayland/mod.rs index 659cb7af17d..224959d5fc8 100644 --- a/src/platform/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -2,7 +2,7 @@ target_os = "netbsd", target_os = "openbsd"))] pub use self::window::Window; -pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorId}; +pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopSink, MonitorHandle}; use sctk::reexports::client::protocol::wl_surface; use sctk::reexports::client::Proxy; diff --git a/src/platform/linux/wayland/pointer.rs b/src/platform_impl/linux/wayland/pointer.rs similarity index 99% rename from src/platform/linux/wayland/pointer.rs rename to src/platform_impl/linux/wayland/pointer.rs index ebe9d101dc3..d910a5ea644 100644 --- a/src/platform/linux/wayland/pointer.rs +++ b/src/platform_impl/linux/wayland/pointer.rs @@ -4,7 +4,7 @@ use {ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent}; use events::ModifiersState; use super::DeviceId; -use super::event_loop::EventsLoopSink; +use super::event_loop::EventLoopSink; use super::window::WindowStore; use sctk::reexports::client::Proxy; @@ -14,7 +14,7 @@ use sctk::reexports::client::protocol::wl_seat::RequestsTrait as SeatRequests; pub fn implement_pointer( seat: &Proxy, - sink: Arc>, + sink: Arc>, store: Arc>, modifiers_tracker: Arc>, ) -> Proxy { diff --git a/src/platform/linux/wayland/touch.rs b/src/platform_impl/linux/wayland/touch.rs similarity index 97% rename from src/platform/linux/wayland/touch.rs rename to src/platform_impl/linux/wayland/touch.rs index e9e28d61547..a357f3aa0b7 100644 --- a/src/platform/linux/wayland/touch.rs +++ b/src/platform_impl/linux/wayland/touch.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex}; use {TouchPhase, WindowEvent}; use super::{DeviceId, WindowId}; -use super::event_loop::EventsLoopSink; +use super::event_loop::EventLoopSink; use super::window::WindowStore; use sctk::reexports::client::Proxy; @@ -19,7 +19,7 @@ struct TouchPoint { pub(crate) fn implement_touch( seat: &Proxy, - sink: Arc>, + sink: Arc>, store: Arc>, ) -> Proxy { let mut pending_ids = Vec::new(); diff --git a/src/platform/linux/wayland/window.rs b/src/platform_impl/linux/wayland/window.rs similarity index 81% rename from src/platform/linux/wayland/window.rs rename to src/platform_impl/linux/wayland/window.rs index 45ae3bd4146..e177e87175a 100644 --- a/src/platform/linux/wayland/window.rs +++ b/src/platform_impl/linux/wayland/window.rs @@ -3,8 +3,13 @@ use std::sync::{Arc, Mutex, Weak}; use {CreationError, MouseCursor, WindowAttributes}; use dpi::{LogicalPosition, LogicalSize}; +<<<<<<< HEAD:src/platform_impl/linux/wayland/window.rs +use platform_impl::MonitorHandle as PlatformMonitorHandle; +use window::MonitorHandle as RootMonitorHandle; +======= use platform::{MonitorId as PlatformMonitorId, PlatformSpecificWindowBuilderAttributes as PlAttributes}; use window::MonitorId as RootMonitorId; +>>>>>>> master:src/platform/linux/wayland/window.rs use sctk::surface::{get_dpi_factor, get_outputs}; use sctk::window::{ConceptFrame, Event as WEvent, Window as SWindow, Theme}; @@ -13,8 +18,8 @@ use sctk::reexports::client::protocol::{wl_seat, wl_surface}; use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests; use sctk::output::OutputMgr; -use super::{make_wid, EventsLoop, MonitorId, WindowId}; -use platform::platform::wayland::event_loop::{get_available_monitors, get_primary_monitor}; +use super::{make_wid, EventLoop, MonitorHandle, WindowId}; +use platform_impl::platform::wayland::event_loop::{get_available_monitors, get_primary_monitor}; pub struct Window { surface: Proxy, @@ -27,7 +32,7 @@ pub struct Window { } impl Window { - pub fn new(evlp: &EventsLoop, attributes: WindowAttributes, pl_attribs: PlAttributes) -> Result { + pub fn new(evlp: &EventLoop, attributes: WindowAttributes, pl_attribs: PlAttributes) -> Result { let (width, height) = attributes.dimensions.map(Into::into).unwrap_or((800, 600)); // Create the window let size = Arc::new(Mutex::new((width, height))); @@ -88,8 +93,8 @@ impl Window { } // Check for fullscreen requirements - if let Some(RootMonitorId { - inner: PlatformMonitorId::Wayland(ref monitor_id), + if let Some(RootMonitorHandle { + inner: PlatformMonitorHandle::Wayland(ref monitor_id), }) = attributes.fullscreen { frame.set_fullscreen(Some(&monitor_id.proxy)); @@ -223,9 +228,9 @@ impl Window { } } - pub fn set_fullscreen(&self, monitor: Option) { - if let Some(RootMonitorId { - inner: PlatformMonitorId::Wayland(ref monitor_id), + pub fn set_fullscreen(&self, monitor: Option) { + if let Some(RootMonitorHandle { + inner: PlatformMonitorHandle::Wayland(ref monitor_id), }) = monitor { self.frame @@ -270,19 +275,27 @@ impl Window { &self.surface } +<<<<<<< HEAD:src/platform_impl/linux/wayland/window.rs + pub fn get_current_monitor(&self) -> MonitorHandle { + // we don't know how much each monitor sees us so... + // just return the most recent one ? + let guard = self.monitors.lock().unwrap(); + guard.monitors.last().unwrap().clone() +======= pub fn get_current_monitor(&self) -> MonitorId { let output = get_outputs(&self.surface).last().unwrap().clone(); MonitorId { proxy: output, mgr: self.outputs.clone(), } +>>>>>>> master:src/platform/linux/wayland/window.rs } - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { get_available_monitors(&self.outputs) } - pub fn get_primary_monitor(&self) -> MonitorId { + pub fn get_primary_monitor(&self) -> MonitorHandle { get_primary_monitor(&self.outputs) } } @@ -388,3 +401,53 @@ impl WindowStore { } } } +<<<<<<< HEAD:src/platform_impl/linux/wayland/window.rs + +/* + * Monitor list with some covenience method to compute DPI + */ + +struct MonitorList { + monitors: Vec +} + +impl MonitorList { + fn new() -> MonitorList { + MonitorList { + monitors: Vec::new() + } + } + + fn compute_hidpi_factor(&self) -> i32 { + let mut factor = 1; + for monitor_id in &self.monitors { + let monitor_dpi = monitor_id.get_hidpi_factor(); + if monitor_dpi > factor { factor = monitor_dpi; } + } + factor + } + + fn add_output(&mut self, monitor: MonitorHandle) -> Option { + let old_dpi = self.compute_hidpi_factor(); + let monitor_dpi = monitor.get_hidpi_factor(); + self.monitors.push(monitor); + if monitor_dpi > old_dpi { + Some(monitor_dpi) + } else { + None + } + } + + fn del_output(&mut self, output: &Proxy) -> Option { + let old_dpi = self.compute_hidpi_factor(); + self.monitors.retain(|m| !m.proxy.equals(output)); + let new_dpi = self.compute_hidpi_factor(); + if new_dpi != old_dpi { + Some(new_dpi) + } else { + None + } + } +} +======= +>>>>>>> master:src/platform/linux/wayland/window.rs diff --git a/src/platform/linux/x11/dnd.rs b/src/platform_impl/linux/x11/dnd.rs similarity index 100% rename from src/platform/linux/x11/dnd.rs rename to src/platform_impl/linux/x11/dnd.rs diff --git a/src/platform/linux/x11/events.rs b/src/platform_impl/linux/x11/events.rs similarity index 100% rename from src/platform/linux/x11/events.rs rename to src/platform_impl/linux/x11/events.rs diff --git a/src/platform/linux/x11/ffi.rs b/src/platform_impl/linux/x11/ffi.rs similarity index 100% rename from src/platform/linux/x11/ffi.rs rename to src/platform_impl/linux/x11/ffi.rs diff --git a/src/platform/linux/x11/ime/callbacks.rs b/src/platform_impl/linux/x11/ime/callbacks.rs similarity index 100% rename from src/platform/linux/x11/ime/callbacks.rs rename to src/platform_impl/linux/x11/ime/callbacks.rs diff --git a/src/platform/linux/x11/ime/context.rs b/src/platform_impl/linux/x11/ime/context.rs similarity index 100% rename from src/platform/linux/x11/ime/context.rs rename to src/platform_impl/linux/x11/ime/context.rs diff --git a/src/platform/linux/x11/ime/inner.rs b/src/platform_impl/linux/x11/ime/inner.rs similarity index 100% rename from src/platform/linux/x11/ime/inner.rs rename to src/platform_impl/linux/x11/ime/inner.rs diff --git a/src/platform/linux/x11/ime/input_method.rs b/src/platform_impl/linux/x11/ime/input_method.rs similarity index 100% rename from src/platform/linux/x11/ime/input_method.rs rename to src/platform_impl/linux/x11/ime/input_method.rs diff --git a/src/platform/linux/x11/ime/mod.rs b/src/platform_impl/linux/x11/ime/mod.rs similarity index 100% rename from src/platform/linux/x11/ime/mod.rs rename to src/platform_impl/linux/x11/ime/mod.rs diff --git a/src/platform/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs similarity index 99% rename from src/platform/linux/x11/mod.rs rename to src/platform_impl/linux/x11/mod.rs index 0200c718154..1cd1bd561ba 100644 --- a/src/platform/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -9,7 +9,7 @@ mod dnd; mod ime; pub mod util; -pub use self::monitor::MonitorId; +pub use self::monitor::MonitorHandle; pub use self::window::UnownedWindow; pub use self::xdisplay::{XConnection, XNotSupported, XError}; @@ -36,7 +36,7 @@ use { CreationError, DeviceEvent, Event, - EventsLoopClosed, + EventLoopClosed, KeyboardInput, LogicalPosition, LogicalSize, @@ -44,11 +44,11 @@ use { WindowEvent, }; use events::ModifiersState; -use platform::PlatformSpecificWindowBuilderAttributes; +use platform_impl::PlatformSpecificWindowBuilderAttributes; use self::dnd::{Dnd, DndState}; use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime}; -pub struct EventsLoop { +pub struct EventLoop { xconn: Arc, wm_delete_window: ffi::Atom, dnd: Dnd, @@ -67,14 +67,14 @@ pub struct EventsLoop { } #[derive(Clone)] -pub struct EventsLoopProxy { +pub struct EventLoopProxy { pending_wakeup: Weak, xconn: Weak, wakeup_dummy_window: ffi::Window, } -impl EventsLoop { - pub fn new(xconn: Arc) -> EventsLoop { +impl EventLoop { + pub fn new(xconn: Arc) -> EventLoop { let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) }; let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") }; @@ -149,7 +149,7 @@ impl EventsLoop { ) }; - let result = EventsLoop { + let result = EventLoop { xconn, wm_delete_window, dnd, @@ -184,8 +184,8 @@ impl EventsLoop { &self.xconn } - pub fn create_proxy(&self) -> EventsLoopProxy { - EventsLoopProxy { + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { pending_wakeup: Arc::downgrade(&self.pending_wakeup), xconn: Arc::downgrade(&self.xconn), wakeup_dummy_window: self.wakeup_dummy_window, @@ -654,7 +654,7 @@ impl EventsLoop { let window = xev.window; let window_id = mkwid(window); - callback(Event::WindowEvent { window_id, event: WindowEvent::Refresh }); + callback(Event::WindowEvent { window_id, event: WindowEvent::Redraw }); } ffi::KeyPress | ffi::KeyRelease => { @@ -1261,15 +1261,15 @@ impl EventsLoop { } } -impl EventsLoopProxy { - pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { - // Update the `EventsLoop`'s `pending_wakeup` flag. +impl EventLoopProxy { + pub fn wakeup(&self) -> Result<(), EventLoopClosed> { + // Update the `EventLoop`'s `pending_wakeup` flag. let display = match (self.pending_wakeup.upgrade(), self.xconn.upgrade()) { (Some(wakeup), Some(display)) => { wakeup.store(true, atomic::Ordering::Relaxed); display }, - _ => return Err(EventsLoopClosed), + _ => return Err(EventLoopClosed), }; // Push an event on the X event queue so that methods run_forever will advance. @@ -1361,7 +1361,7 @@ impl Deref for Window { impl Window { pub fn new( - event_loop: &EventsLoop, + event_loop: &EventLoop, attribs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes ) -> Result { @@ -1446,7 +1446,7 @@ enum ScrollOrientation { } impl Device { - fn new(el: &EventsLoop, info: &ffi::XIDeviceInfo) -> Self { + fn new(el: &EventLoop, info: &ffi::XIDeviceInfo) -> Self { let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() }; let mut scroll_axes = Vec::new(); diff --git a/src/platform/linux/x11/monitor.rs b/src/platform_impl/linux/x11/monitor.rs similarity index 94% rename from src/platform/linux/x11/monitor.rs rename to src/platform_impl/linux/x11/monitor.rs index cf1f456c462..49549805307 100644 --- a/src/platform/linux/x11/monitor.rs +++ b/src/platform_impl/linux/x11/monitor.rs @@ -20,7 +20,7 @@ const DISABLE_MONITOR_LIST_CACHING: bool = false; lazy_static! { static ref XRANDR_VERSION: Mutex> = Mutex::default(); - static ref MONITORS: Mutex>> = Mutex::default(); + static ref MONITORS: Mutex>> = Mutex::default(); } fn version_is_at_least(major: c_int, minor: c_int) -> bool { @@ -35,13 +35,13 @@ fn version_is_at_least(major: c_int, minor: c_int) -> bool { } } -pub fn invalidate_cached_monitor_list() -> Option> { +pub fn invalidate_cached_monitor_list() -> Option> { // We update this lazily. (*MONITORS.lock()).take() } #[derive(Debug, Clone)] -pub struct MonitorId { +pub struct MonitorHandle { /// The actual id id: u32, /// The name of the monitor @@ -58,7 +58,7 @@ pub struct MonitorId { pub(crate) rect: util::AaRect, } -impl MonitorId { +impl MonitorHandle { fn from_repr( xconn: &XConnection, resources: *mut XRRScreenResources, @@ -69,7 +69,7 @@ impl MonitorId { let (name, hidpi_factor) = unsafe { xconn.get_output_info(resources, &repr)? }; let (dimensions, position) = unsafe { (repr.get_dimensions(), repr.get_position()) }; let rect = util::AaRect::new(position, dimensions); - Some(MonitorId { + Some(MonitorHandle { id, name, hidpi_factor, @@ -104,7 +104,7 @@ impl MonitorId { } impl XConnection { - pub fn get_monitor_for_window(&self, window_rect: Option) -> MonitorId { + pub fn get_monitor_for_window(&self, window_rect: Option) -> MonitorHandle { let monitors = self.get_available_monitors(); let default = monitors .get(0) @@ -128,7 +128,7 @@ impl XConnection { matched_monitor.to_owned() } - fn query_monitor_list(&self) -> Vec { + fn query_monitor_list(&self) -> Vec { unsafe { let root = (self.xlib.XDefaultRootWindow)(self.display); let resources = if version_is_at_least(1, 3) { @@ -158,7 +158,7 @@ impl XConnection { let monitor = monitors.offset(monitor_index as isize); let is_primary = (*monitor).primary != 0; has_primary |= is_primary; - MonitorId::from_repr( + MonitorHandle::from_repr( self, resources, monitor_index as u32, @@ -181,7 +181,7 @@ impl XConnection { let crtc = util::MonitorRepr::from(crtc); let is_primary = crtc.get_output() == primary; has_primary |= is_primary; - MonitorId::from_repr( + MonitorHandle::from_repr( self, resources, crtc_id as u32, @@ -206,7 +206,7 @@ impl XConnection { } } - pub fn get_available_monitors(&self) -> Vec { + pub fn get_available_monitors(&self) -> Vec { let mut monitors_lock = MONITORS.lock(); (*monitors_lock) .as_ref() @@ -222,7 +222,7 @@ impl XConnection { } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { + pub fn get_primary_monitor(&self) -> MonitorHandle { self.get_available_monitors() .into_iter() .find(|monitor| monitor.primary) diff --git a/src/platform/linux/x11/util/atom.rs b/src/platform_impl/linux/x11/util/atom.rs similarity index 100% rename from src/platform/linux/x11/util/atom.rs rename to src/platform_impl/linux/x11/util/atom.rs diff --git a/src/platform/linux/x11/util/client_msg.rs b/src/platform_impl/linux/x11/util/client_msg.rs similarity index 100% rename from src/platform/linux/x11/util/client_msg.rs rename to src/platform_impl/linux/x11/util/client_msg.rs diff --git a/src/platform/linux/x11/util/format.rs b/src/platform_impl/linux/x11/util/format.rs similarity index 100% rename from src/platform/linux/x11/util/format.rs rename to src/platform_impl/linux/x11/util/format.rs diff --git a/src/platform/linux/x11/util/geometry.rs b/src/platform_impl/linux/x11/util/geometry.rs similarity index 100% rename from src/platform/linux/x11/util/geometry.rs rename to src/platform_impl/linux/x11/util/geometry.rs diff --git a/src/platform/linux/x11/util/hint.rs b/src/platform_impl/linux/x11/util/hint.rs similarity index 100% rename from src/platform/linux/x11/util/hint.rs rename to src/platform_impl/linux/x11/util/hint.rs diff --git a/src/platform/linux/x11/util/icon.rs b/src/platform_impl/linux/x11/util/icon.rs similarity index 100% rename from src/platform/linux/x11/util/icon.rs rename to src/platform_impl/linux/x11/util/icon.rs diff --git a/src/platform/linux/x11/util/input.rs b/src/platform_impl/linux/x11/util/input.rs similarity index 100% rename from src/platform/linux/x11/util/input.rs rename to src/platform_impl/linux/x11/util/input.rs diff --git a/src/platform/linux/x11/util/memory.rs b/src/platform_impl/linux/x11/util/memory.rs similarity index 100% rename from src/platform/linux/x11/util/memory.rs rename to src/platform_impl/linux/x11/util/memory.rs diff --git a/src/platform/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs similarity index 100% rename from src/platform/linux/x11/util/mod.rs rename to src/platform_impl/linux/x11/util/mod.rs diff --git a/src/platform/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs similarity index 100% rename from src/platform/linux/x11/util/randr.rs rename to src/platform_impl/linux/x11/util/randr.rs diff --git a/src/platform/linux/x11/util/window_property.rs b/src/platform_impl/linux/x11/util/window_property.rs similarity index 100% rename from src/platform/linux/x11/util/window_property.rs rename to src/platform_impl/linux/x11/util/window_property.rs diff --git a/src/platform/linux/x11/util/wm.rs b/src/platform_impl/linux/x11/util/wm.rs similarity index 100% rename from src/platform/linux/x11/util/wm.rs rename to src/platform_impl/linux/x11/util/wm.rs diff --git a/src/platform/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs similarity index 98% rename from src/platform/linux/x11/window.rs rename to src/platform_impl/linux/x11/window.rs index 8a5f711eae6..db82e0e2a01 100644 --- a/src/platform/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -10,12 +10,12 @@ use parking_lot::Mutex; use {Icon, MouseCursor, WindowAttributes}; use CreationError::{self, OsError}; use dpi::{LogicalPosition, LogicalSize}; -use platform::MonitorId as PlatformMonitorId; -use platform::PlatformSpecificWindowBuilderAttributes; -use platform::x11::MonitorId as X11MonitorId; -use window::MonitorId as RootMonitorId; +use platform_impl::MonitorHandle as PlatformMonitorHandle; +use platform_impl::PlatformSpecificWindowBuilderAttributes; +use platform_impl::x11::MonitorHandle as X11MonitorHandle; +use window::MonitorHandle as RootMonitorHandle; -use super::{ffi, util, ImeSender, XConnection, XError, WindowId, EventsLoop}; +use super::{ffi, util, ImeSender, XConnection, XError, WindowId, EventLoop}; unsafe extern "C" fn visibility_predicate( _display: *mut ffi::Display, @@ -35,7 +35,7 @@ pub struct SharedState { pub inner_position: Option<(i32, i32)>, pub inner_position_rel_parent: Option<(i32, i32)>, pub guessed_dpi: Option, - pub last_monitor: Option, + pub last_monitor: Option, pub dpi_adjusted: Option<(f64, f64)>, // Used to restore position after exiting fullscreen. pub restore_position: Option<(i32, i32)>, @@ -70,7 +70,7 @@ pub struct UnownedWindow { impl UnownedWindow { pub fn new( - event_loop: &EventsLoop, + event_loop: &EventLoop, window_attrs: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes, ) -> Result { @@ -513,7 +513,7 @@ impl UnownedWindow { self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0)) } - fn set_fullscreen_inner(&self, monitor: Option) -> util::Flusher { + fn set_fullscreen_inner(&self, monitor: Option) -> util::Flusher { match monitor { None => { let flusher = self.set_fullscreen_hint(false); @@ -522,7 +522,7 @@ impl UnownedWindow { } flusher }, - Some(RootMonitorId { inner: PlatformMonitorId::X(monitor) }) => { + Some(RootMonitorHandle { inner: PlatformMonitorHandle::X(monitor) }) => { let window_position = self.get_position_physical(); self.shared_state.lock().restore_position = window_position; let monitor_origin: (i32, i32) = monitor.get_position().into(); @@ -534,7 +534,7 @@ impl UnownedWindow { } #[inline] - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, monitor: Option) { self.set_fullscreen_inner(monitor) .flush() .expect("Failed to change window fullscreen state"); @@ -551,7 +551,7 @@ impl UnownedWindow { } #[inline] - pub fn get_current_monitor(&self) -> X11MonitorId { + pub fn get_current_monitor(&self) -> X11MonitorHandle { let monitor = self.shared_state .lock() .last_monitor @@ -565,11 +565,11 @@ impl UnownedWindow { }) } - pub fn get_available_monitors(&self) -> Vec { + pub fn get_available_monitors(&self) -> Vec { self.xconn.get_available_monitors() } - pub fn get_primary_monitor(&self) -> X11MonitorId { + pub fn get_primary_monitor(&self) -> X11MonitorHandle { self.xconn.get_primary_monitor() } diff --git a/src/platform/linux/x11/xdisplay.rs b/src/platform_impl/linux/x11/xdisplay.rs similarity index 100% rename from src/platform/linux/x11/xdisplay.rs rename to src/platform_impl/linux/x11/xdisplay.rs diff --git a/src/platform/macos/events_loop.rs b/src/platform_impl/macos/event_loop.rs similarity index 98% rename from src/platform/macos/events_loop.rs rename to src/platform_impl/macos/event_loop.rs index 74532a2747c..42ad8cb4337 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform_impl/macos/event_loop.rs @@ -1,4 +1,4 @@ -use {ControlFlow, EventsLoopClosed}; +use {ControlFlow, EventLoopClosed}; use cocoa::{self, appkit, foundation}; use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow}; use events::{self, ElementState, Event, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput}; @@ -9,12 +9,12 @@ use std; use std::os::raw::*; use super::DeviceId; -pub struct EventsLoop { +pub struct EventLoop { modifiers: Modifiers, pub shared: Arc, } -// State shared between the `EventsLoop` and its registered windows. +// State shared between the `EventLoop` and its registered windows. pub struct Shared { pub windows: Mutex>>, pub pending_events: Mutex>, @@ -42,7 +42,7 @@ struct Modifiers { // Wrapping the user callback in a type allows us to: // // - ensure the callback pointer is never accidentally cloned -// - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer +// - ensure that only the `EventLoop` can `store` and `drop` the callback pointer // - Share access to the user callback with the NSWindow callbacks. pub struct UserCallback { mutex: Mutex>, @@ -160,17 +160,17 @@ impl UserCallback { } -impl EventsLoop { +impl EventLoop { pub fn new() -> Self { // Mark this thread as the main thread of the Cocoa event system. // // This must be done before any worker threads get a chance to call it - // (e.g., via `EventsLoopProxy::wakeup()`), causing a wrong thread to be + // (e.g., via `EventLoopProxy::wakeup()`), causing a wrong thread to be // marked as the main thread. unsafe { appkit::NSApp(); } - EventsLoop { + EventLoop { shared: Arc::new(Shared::new()), modifiers: Modifiers::new(), } @@ -521,7 +521,7 @@ impl EventsLoop { } impl Proxy { - pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { + pub fn wakeup(&self) -> Result<(), EventLoopClosed> { // Awaken the event loop by triggering `NSApplicationActivatedEventType`. unsafe { let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil); diff --git a/src/platform/macos/ffi.rs b/src/platform_impl/macos/ffi.rs similarity index 100% rename from src/platform/macos/ffi.rs rename to src/platform_impl/macos/ffi.rs diff --git a/src/platform/macos/mod.rs b/src/platform_impl/macos/mod.rs similarity index 76% rename from src/platform/macos/mod.rs rename to src/platform_impl/macos/mod.rs index 1091648ef48..5a87c54cb78 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform_impl/macos/mod.rs @@ -1,7 +1,7 @@ #![cfg(target_os = "macos")] -pub use self::events_loop::{EventsLoop, Proxy as EventsLoopProxy}; -pub use self::monitor::MonitorId; +pub use self::event_loop::{EventLoop, Proxy as EventLoopProxy}; +pub use self::monitor::MonitorHandle; pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window2}; use std::sync::Arc; @@ -30,20 +30,20 @@ impl ::std::ops::Deref for Window { impl Window { - pub fn new(events_loop: &EventsLoop, + pub fn new(event_loop: &EventLoop, attributes: ::WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result { - let weak_shared = Arc::downgrade(&events_loop.shared); + let weak_shared = Arc::downgrade(&event_loop.shared); let window = Arc::new(try!(Window2::new(weak_shared, attributes, pl_attribs))); let weak_window = Arc::downgrade(&window); - events_loop.shared.windows.lock().unwrap().push(weak_window); + event_loop.shared.windows.lock().unwrap().push(weak_window); Ok(Window { window: window }) } } -mod events_loop; +mod event_loop; mod ffi; mod monitor; mod util; diff --git a/src/platform/macos/monitor.rs b/src/platform_impl/macos/monitor.rs similarity index 81% rename from src/platform/macos/monitor.rs rename to src/platform_impl/macos/monitor.rs index c8786f9dce0..7b3e848832e 100644 --- a/src/platform/macos/monitor.rs +++ b/src/platform_impl/macos/monitor.rs @@ -7,17 +7,17 @@ use cocoa::foundation::{NSString, NSUInteger}; use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds}; use {PhysicalPosition, PhysicalSize}; -use super::EventsLoop; +use super::EventLoop; use super::window::{IdRef, Window2}; #[derive(Clone, PartialEq)] -pub struct MonitorId(CGDirectDisplayID); +pub struct MonitorHandle(CGDirectDisplayID); -fn get_available_monitors() -> VecDeque { +fn get_available_monitors() -> VecDeque { if let Ok(displays) = CGDisplay::active_displays() { let mut monitors = VecDeque::with_capacity(displays.len()); for d in displays { - monitors.push_back(MonitorId(d)); + monitors.push_back(MonitorHandle(d)); } monitors } else { @@ -25,44 +25,44 @@ fn get_available_monitors() -> VecDeque { } } -pub fn get_primary_monitor() -> MonitorId { - let id = MonitorId(CGDisplay::main().id); +pub fn get_primary_monitor() -> MonitorHandle { + let id = MonitorHandle(CGDisplay::main().id); id } -impl EventsLoop { +impl EventLoop { #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { get_available_monitors() } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { + pub fn get_primary_monitor(&self) -> MonitorHandle { get_primary_monitor() } - pub fn make_monitor_from_display(id: CGDirectDisplayID) -> MonitorId { - let id = MonitorId(id); + pub fn make_monitor_from_display(id: CGDirectDisplayID) -> MonitorHandle { + let id = MonitorHandle(id); id } } impl Window2 { #[inline] - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { get_available_monitors() } #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { + pub fn get_primary_monitor(&self) -> MonitorHandle { get_primary_monitor() } } -impl fmt::Debug for MonitorId { +impl fmt::Debug for MonitorHandle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[derive(Debug)] - struct MonitorId { + struct MonitorHandle { name: Option, native_identifier: u32, dimensions: PhysicalSize, @@ -70,7 +70,7 @@ impl fmt::Debug for MonitorId { hidpi_factor: f64, } - let monitor_id_proxy = MonitorId { + let monitor_id_proxy = MonitorHandle { name: self.get_name(), native_identifier: self.get_native_identifier(), dimensions: self.get_dimensions(), @@ -82,9 +82,9 @@ impl fmt::Debug for MonitorId { } } -impl MonitorId { +impl MonitorHandle { pub fn get_name(&self) -> Option { - let MonitorId(display_id) = *self; + let MonitorHandle(display_id) = *self; let screen_num = CGDisplay::new(display_id).model_number(); Some(format!("Monitor #{}", screen_num)) } @@ -95,7 +95,7 @@ impl MonitorId { } pub fn get_dimensions(&self) -> PhysicalSize { - let MonitorId(display_id) = *self; + let MonitorHandle(display_id) = *self; let display = CGDisplay::new(display_id); let height = display.pixels_high(); let width = display.pixels_wide(); diff --git a/src/platform_impl/macos/util.rs b/src/platform_impl/macos/util.rs new file mode 100644 index 00000000000..c4c348f036b --- /dev/null +++ b/src/platform_impl/macos/util.rs @@ -0,0 +1,38 @@ +use cocoa::appkit::NSWindowStyleMask; +use cocoa::base::{id, nil}; +use cocoa::foundation::{NSRect, NSUInteger}; +use core_graphics::display::CGDisplay; + +use platform_impl::platform::ffi; +use platform_impl::platform::window::IdRef; + +pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange { + location: ffi::NSNotFound as NSUInteger, + length: 0, +}; + +// For consistency with other platforms, this will... +// 1. translate the bottom-left window corner into the top-left window corner +// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one +pub fn bottom_left_to_top_left(rect: NSRect) -> f64 { + CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height) +} + +pub unsafe fn set_style_mask(window: id, view: id, mask: NSWindowStyleMask) { + use cocoa::appkit::NSWindow; + window.setStyleMask_(mask); + // If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly! + window.makeFirstResponder_(view); +} + +pub unsafe fn create_input_context(view: id) -> IdRef { + let input_context: id = msg_send![class!(NSTextInputContext), alloc]; + let input_context: id = msg_send![input_context, initWithClient:view]; + IdRef::new(input_context) +} + +#[allow(dead_code)] +pub unsafe fn open_emoji_picker() { + let app: id = msg_send![class!(NSApplication), sharedApplication]; + let _: () = msg_send![app, orderFrontCharacterPalette:nil]; +} diff --git a/src/platform/macos/view.rs b/src/platform_impl/macos/view.rs similarity index 98% rename from src/platform/macos/view.rs rename to src/platform_impl/macos/view.rs index 54d44532faa..ba8ed5793a7 100644 --- a/src/platform/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -14,11 +14,10 @@ use objc::declare::ClassDecl; use objc::runtime::{Class, Object, Protocol, Sel, BOOL, YES}; use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId}; -use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, scancode_to_keycode, char_to_keycode, check_function_keys, get_scancode}; -use platform::platform::util; -use platform::platform::ffi::*; -use platform::platform::window::{get_window_id, IdRef}; -use events; +use platform_impl::platform::event_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code, check_additional_virtual_key_codes}; +use platform_impl::platform::util; +use platform_impl::platform::ffi::*; +use platform_impl::platform::window::{get_window_id, IdRef}; struct ViewState { window: id, diff --git a/src/platform/macos/window.rs b/src/platform_impl/macos/window.rs similarity index 98% rename from src/platform/macos/window.rs rename to src/platform_impl/macos/window.rs index 4fc27328a31..34edc55161d 100644 --- a/src/platform/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -42,10 +42,10 @@ use { }; use CreationError::OsError; use os::macos::{ActivationPolicy, WindowExt}; -use platform::platform::{ffi, util}; -use platform::platform::events_loop::{EventsLoop, Shared}; -use platform::platform::view::{new_view, set_ime_spot}; -use window::MonitorId as RootMonitorId; +use platform_impl::platform::{ffi, util}; +use platform_impl::platform::event_loop::{EventLoop, Shared}; +use platform_impl::platform::view::{new_view, set_ime_spot}; +use window::MonitorHandle as RootMonitorHandle; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Id(pub usize); @@ -183,7 +183,7 @@ pub struct WindowDelegate { } impl WindowDelegate { - // Emits an event via the `EventsLoop`'s callback or stores it in the pending queue. + // Emits an event via the `EventLoop`'s callback or stores it in the pending queue. pub fn emit_event(state: &mut DelegateState, window_event: WindowEvent) { let window_id = get_window_id(*state.window); let event = Event::WindowEvent { @@ -554,13 +554,13 @@ pub struct Window2 { unsafe impl Send for Window2 {} unsafe impl Sync for Window2 {} -unsafe fn get_current_monitor(window: id) -> RootMonitorId { +unsafe fn get_current_monitor(window: id) -> RootMonitorHandle { let screen: id = msg_send![window, screen]; let desc = NSScreen::deviceDescription(screen); let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber")); let value = NSDictionary::valueForKey_(desc, *key); let display_id = msg_send![value, unsignedIntegerValue]; - RootMonitorId { inner: EventsLoop::make_monitor_from_display(display_id) } + RootMonitorHandle { inner: EventLoop::make_monitor_from_display(display_id) } } impl Drop for Window2 { @@ -1140,7 +1140,7 @@ impl Window2 { #[inline] /// TODO: Right now set_fullscreen do not work on switching monitors /// in fullscreen mode - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, monitor: Option) { let state = &self.delegate.state; // Do nothing if simple fullscreen is active. @@ -1248,7 +1248,7 @@ impl Window2 { } #[inline] - pub fn get_current_monitor(&self) -> RootMonitorId { + pub fn get_current_monitor(&self) -> RootMonitorHandle { unsafe { self::get_current_monitor(*self.window) } diff --git a/src/platform_impl/mod.rs b/src/platform_impl/mod.rs new file mode 100644 index 00000000000..be968785004 --- /dev/null +++ b/src/platform_impl/mod.rs @@ -0,0 +1,26 @@ +pub use self::platform::*; + +#[cfg(target_os = "windows")] +#[path="windows/mod.rs"] +mod platform; +#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#[path="linux/mod.rs"] +mod platform; +#[cfg(target_os = "macos")] +#[path="macos/mod.rs"] +mod platform; +#[cfg(target_os = "android")] +#[path="android/mod.rs"] +mod platform; +#[cfg(target_os = "ios")] +#[path="ios/mod.rs"] +mod platform; +#[cfg(target_os = "emscripten")] +#[path="emscripten/mod.rs"] +mod platform; + +#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"), + not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"), + not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd"), + not(target_os = "emscripten")))] +compile_error!("The platform you're compiling for is not supported by winit"); diff --git a/src/platform/windows/dpi.rs b/src/platform_impl/windows/dpi.rs similarity index 100% rename from src/platform/windows/dpi.rs rename to src/platform_impl/windows/dpi.rs diff --git a/src/platform/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs similarity index 91% rename from src/platform/windows/drop_handler.rs rename to src/platform_impl/windows/drop_handler.rs index 9b2af7ce5a0..6895bf306a4 100644 --- a/src/platform/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -14,16 +14,17 @@ use winapi::um::oleidl::{DROPEFFECT_COPY, DROPEFFECT_NONE, IDropTarget, IDropTar use winapi::um::winnt::HRESULT; use winapi::um::{shellapi, unknwnbase}; -use platform::platform::events_loop::send_event; -use platform::platform::WindowId; +use platform_impl::platform::WindowId; -use {Event, WindowId as SuperWindowId}; +use event::Event; +use window::WindowId as SuperWindowId; #[repr(C)] pub struct FileDropHandlerData { pub interface: IDropTarget, refcount: AtomicUsize, window: HWND, + send_event: Box)>, cursor_effect: DWORD, hovered_is_valid: bool, // If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted } @@ -34,13 +35,14 @@ pub struct FileDropHandler { #[allow(non_snake_case)] impl FileDropHandler { - pub fn new(window: HWND) -> FileDropHandler { + pub fn new(window: HWND, send_event: Box)>) -> FileDropHandler { let data = Box::new(FileDropHandlerData { interface: IDropTarget { lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl, }, refcount: AtomicUsize::new(1), window, + send_event, cursor_effect: DROPEFFECT_NONE, hovered_is_valid: false, }); @@ -83,10 +85,10 @@ impl FileDropHandler { _pt: *const POINTL, pdwEffect: *mut DWORD, ) -> HRESULT { - use events::WindowEvent::HoveredFile; + use event::WindowEvent::HoveredFile; let drop_handler = Self::from_interface(this); let hdrop = Self::iterate_filenames(pDataObj, |filename| { - send_event(Event::WindowEvent { + drop_handler.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(drop_handler.window)), event: HoveredFile(filename), }); @@ -115,10 +117,10 @@ impl FileDropHandler { } pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT { - use events::WindowEvent::HoveredFileCancelled; + use event::WindowEvent::HoveredFileCancelled; let drop_handler = Self::from_interface(this); if drop_handler.hovered_is_valid { - send_event(Event::WindowEvent { + drop_handler.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(drop_handler.window)), event: HoveredFileCancelled, }); @@ -134,10 +136,10 @@ impl FileDropHandler { _pt: *const POINTL, _pdwEffect: *mut DWORD, ) -> HRESULT { - use events::WindowEvent::DroppedFile; + use event::WindowEvent::DroppedFile; let drop_handler = Self::from_interface(this); let hdrop = Self::iterate_filenames(pDataObj, |filename| { - send_event(Event::WindowEvent { + drop_handler.send_event(Event::WindowEvent { window_id: SuperWindowId(WindowId(drop_handler.window)), event: DroppedFile(filename), }); @@ -205,6 +207,12 @@ impl FileDropHandler { } } +impl FileDropHandlerData { + fn send_event(&self, event: Event<()>) { + (self.send_event)(event); + } +} + impl Drop for FileDropHandler { fn drop(&mut self) { unsafe { diff --git a/src/platform/windows/event.rs b/src/platform_impl/windows/event.rs similarity index 98% rename from src/platform/windows/event.rs rename to src/platform_impl/windows/event.rs index 1546f8efe83..347a0795677 100644 --- a/src/platform/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -2,20 +2,11 @@ use std::{char, ptr}; use std::os::raw::c_int; use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; -use events::VirtualKeyCode; -use events::ModifiersState; +use event::{ScanCode, ModifiersState, VirtualKeyCode}; use winapi::shared::minwindef::{WPARAM, LPARAM, UINT, HKL, HKL__}; use winapi::um::winuser; -use ScanCode; - -fn key_pressed(vkey: c_int) -> bool { - unsafe { - (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) - } -} - pub fn get_key_mods() -> ModifiersState { let mut mods = ModifiersState::default(); let filter_out_altgr = layout_uses_altgr() && key_pressed(winuser::VK_RMENU); diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs new file mode 100644 index 00000000000..9bc874b6fd0 --- /dev/null +++ b/src/platform_impl/windows/event_loop.rs @@ -0,0 +1,1597 @@ +//! 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 winapi::shared::basetsd::DWORD_PTR; +use winapi::shared::basetsd::UINT_PTR; +use std::{mem, panic, ptr}; +use std::any::Any; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::{self, Sender, Receiver}; +use std::time::{Duration, Instant}; +use std::rc::Rc; +use std::cell::RefCell; +use std::collections::VecDeque; +use std::marker::PhantomData; +use parking_lot::Mutex; + +use winapi::ctypes::c_int; +use winapi::shared::minwindef::{ + BOOL, + DWORD, + HIWORD, + INT, + LOWORD, + LPARAM, + LRESULT, + UINT, + WPARAM, +}; +use winapi::shared::windef::{HWND, POINT, RECT}; +use winapi::shared::{windowsx, winerror}; +use winapi::um::{winuser, winbase, ole2, processthreadsapi, commctrl, libloaderapi}; +use winapi::um::winnt::{LONG, LPCSTR, SHORT}; + +use window::WindowId as RootWindowId; +use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; +use dpi::{LogicalPosition, LogicalSize, PhysicalSize}; +use event::{DeviceEvent, Touch, TouchPhase, StartCause, KeyboardInput, Event, WindowEvent}; +use platform_impl::platform::{event, WindowId, DEVICE_ID, wrap_device_id, util}; +use platform_impl::platform::dpi::{ + become_dpi_aware, + dpi_to_scale_factor, + enable_non_client_dpi_scaling, + get_hwnd_scale_factor, +}; +use platform_impl::platform::drop_handler::FileDropHandler; +use platform_impl::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey}; +use platform_impl::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state}; +use platform_impl::platform::window::adjust_size; +use platform_impl::platform::window_state::{CursorFlags, WindowFlags, WindowState}; + +pub(crate) struct SubclassInput { + pub window_state: Arc>, + pub event_loop_runner: EventLoopRunnerShared, + pub file_drop_handler: FileDropHandler, +} + +impl SubclassInput { + unsafe fn send_event(&self, event: Event) { + self.event_loop_runner.send_event(event); + } +} + +struct ThreadMsgTargetSubclassInput { + event_loop_runner: EventLoopRunnerShared, + user_event_receiver: Receiver, +} + +impl ThreadMsgTargetSubclassInput { + unsafe fn send_event(&self, event: Event) { + self.event_loop_runner.send_event(event); + } +} + +pub struct EventLoop { + thread_msg_sender: Sender, + window_target: RootELW, +} + +pub struct EventLoopWindowTarget { + thread_id: DWORD, + trigger_newevents_on_redraw: Arc, + thread_msg_target: HWND, + pub(crate) runner_shared: EventLoopRunnerShared, +} + +impl EventLoop { + pub fn new() -> EventLoop { + Self::with_dpi_awareness(true) + } + + pub fn window_target(&self) -> &RootELW { + &self.window_target + } + + pub fn with_dpi_awareness(dpi_aware: bool) -> EventLoop { + become_dpi_aware(dpi_aware); + + let thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; + let runner_shared = Rc::new(ELRShared { + runner: RefCell::new(None), + buffer: RefCell::new(VecDeque::new()), + }); + let (thread_msg_target, thread_msg_sender) = thread_event_target_window(runner_shared.clone()); + + EventLoop { + thread_msg_sender, + window_target: RootELW { + p: EventLoopWindowTarget { + thread_id, + trigger_newevents_on_redraw: Arc::new(AtomicBool::new(true)), + thread_msg_target, + runner_shared, + }, + _marker: PhantomData, + }, + } + } + + pub fn run(mut self, event_handler: F) -> ! + where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) + { + self.run_return(event_handler); + ::std::process::exit(0); + } + + pub fn run_return(&mut self, mut event_handler: F) + where F: FnMut(Event, &RootELW, &mut ControlFlow) + { + unsafe{ winuser::IsGUIThread(1); } + + let event_loop_windows_ref = &self.window_target; + + let mut runner = unsafe{ EventLoopRunner::new( + self, + move |event, control_flow| { + event_handler(event, event_loop_windows_ref, control_flow) + } + ) }; + { + let runner_shared = self.window_target.p.runner_shared.clone(); + let mut runner_ref = runner_shared.runner.borrow_mut(); + loop { + let event = runner_shared.buffer.borrow_mut().pop_front(); + match event { + Some(e) => { runner.process_event(e); }, + None => break + } + } + *runner_ref = Some(runner); + } + + macro_rules! runner { + () => { self.window_target.p.runner_shared.runner.borrow_mut().as_mut().unwrap() }; + } + + unsafe { + let mut msg = mem::uninitialized(); + let mut msg_unprocessed = false; + + 'main: loop { + runner!().new_events(); + loop { + if !msg_unprocessed { + if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1) { + break; + } + } + winuser::TranslateMessage(&mut msg); + winuser::DispatchMessageW(&mut msg); + msg_unprocessed = false; + } + runner!().events_cleared(); + if let Some(payload) = runner!().panic_error.take() { + panic::resume_unwind(payload); + } + + let control_flow = runner!().control_flow; + match control_flow { + ControlFlow::Exit => break 'main, + ControlFlow::Wait => { + if 0 == winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) { + break 'main + } + msg_unprocessed = true; + } + ControlFlow::WaitUntil(resume_time) => { + wait_until_time_or_msg(resume_time); + }, + ControlFlow::Poll => () + } + } + } + + runner!().call_event_handler(Event::LoopDestroyed); + *self.window_target.p.runner_shared.runner.borrow_mut() = None; + } + + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { + target_window: self.window_target.p.thread_msg_target, + event_send: self.thread_msg_sender.clone(), + } + } +} + +impl EventLoopWindowTarget { + #[inline(always)] + pub(crate) fn create_thread_executor(&self) -> EventLoopThreadExecutor { + EventLoopThreadExecutor { + thread_id: self.thread_id, + trigger_newevents_on_redraw: self.trigger_newevents_on_redraw.clone(), + target_window: self.thread_msg_target, + } + } +} + +pub(crate) type EventLoopRunnerShared = Rc>; +pub(crate) struct ELRShared { + runner: RefCell>>, + buffer: RefCell>>, +} +pub(crate) struct EventLoopRunner { + trigger_newevents_on_redraw: Arc, + control_flow: ControlFlow, + runner_state: RunnerState, + modal_redraw_window: HWND, + in_modal_loop: bool, + event_handler: Box, &mut ControlFlow)>, + panic_error: Option, +} +type PanicError = Box; + +impl ELRShared { + pub(crate) unsafe fn send_event(&self, event: Event) { + if let Ok(mut runner_ref) = self.runner.try_borrow_mut() { + if let Some(ref mut runner) = *runner_ref { + runner.process_event(event); + + // Dispatch any events that were buffered during the call to `process_event`. + loop { + // We do this instead of using a `while let` loop because if we use a `while let` + // loop the reference returned `borrow_mut()` doesn't get dropped until the end + // of the loop's body and attempts to add events to the event buffer while in + // `process_event` will fail. + let buffered_event_opt = self.buffer.borrow_mut().pop_front(); + match buffered_event_opt { + Some(event) => runner.process_event(event), + None => break + } + } + + return; + } + } + + // If the runner is already borrowed, we're in the middle of an event loop invocation. Add + // the event to a buffer to be processed later. + self.buffer.borrow_mut().push_back(event) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum RunnerState { + /// The event loop has just been created, and an `Init` event must be sent. + New, + /// The event loop is idling, and began idling at the given instant. + Idle(Instant), + /// The event loop has received a signal from the OS that the loop may resume, but no winit + /// events have been generated yet. We're waiting for an event to be processed or the events + /// to be marked as cleared to send `NewEvents`, depending on the current `ControlFlow`. + DeferredNewEvents(Instant), + /// The event loop is handling the OS's events and sending them to the user's callback. + /// `NewEvents` has been sent, and `EventsCleared` hasn't. + HandlingEvents, +} + +impl EventLoopRunner { + unsafe fn new(event_loop: &EventLoop, f: F) -> EventLoopRunner + where F: FnMut(Event, &mut ControlFlow) + { + EventLoopRunner { + trigger_newevents_on_redraw: event_loop.window_target.p.trigger_newevents_on_redraw.clone(), + control_flow: ControlFlow::default(), + runner_state: RunnerState::New, + in_modal_loop: false, + modal_redraw_window: event_loop.window_target.p.thread_msg_target, + event_handler: mem::transmute::< + Box, &mut ControlFlow)>, + Box, &mut ControlFlow)> + >(Box::new(f)), + panic_error: None, + } + } + + fn new_events(&mut self) { + self.runner_state = match self.runner_state { + // If we're already handling events or have deferred `NewEvents`, we don't need to do + // do any processing. + RunnerState::HandlingEvents | + RunnerState::DeferredNewEvents(..) => self.runner_state, + + // Send the `Init` `NewEvents` and immediately move into event processing. + RunnerState::New => { + self.call_event_handler(Event::NewEvents(StartCause::Init)); + RunnerState::HandlingEvents + }, + + // When `NewEvents` gets sent after an idle depends on the control flow... + RunnerState::Idle(wait_start) => { + match self.control_flow { + // If we're polling, send `NewEvents` and immediately move into event processing. + ControlFlow::Poll => { + self.call_event_handler(Event::NewEvents(StartCause::Poll)); + RunnerState::HandlingEvents + }, + // If the user was waiting until a specific time, the `NewEvents` call gets sent + // at varying times depending on the current time. + ControlFlow::WaitUntil(resume_time) => { + match Instant::now() >= resume_time { + // If the current time is later than the requested resume time, we can tell the + // user that the resume time has been reached with `NewEvents` and immdiately move + // into event processing. + true => { + self.call_event_handler(Event::NewEvents(StartCause::ResumeTimeReached { + start: wait_start, + requested_resume: resume_time, + })); + RunnerState::HandlingEvents + }, + // However, if the current time is EARLIER than the requested resume time, we + // don't want to send the `WaitCancelled` event until we know an event is being + // sent. Defer. + false => RunnerState::DeferredNewEvents(wait_start) + } + }, + // If we're waiting, `NewEvents` doesn't get sent until winit gets an event, so + // we defer. + ControlFlow::Wait | + // `Exit` shouldn't really ever get sent here, but if it does do something somewhat sane. + ControlFlow::Exit => RunnerState::DeferredNewEvents(wait_start), + } + } + }; + } + + fn process_event(&mut self, event: Event) { + // If we're in the modal loop, we need to have some mechanism for finding when the event + // queue has been cleared so we can call `events_cleared`. Windows doesn't give any utilities + // for doing this, but it DOES guarantee that WM_PAINT will only occur after input events have + // been processed. So, we send WM_PAINT to a dummy window which calls `events_cleared` when + // the events queue has been emptied. + if self.in_modal_loop { + unsafe { + winuser::RedrawWindow( + self.modal_redraw_window, + ptr::null(), + ptr::null_mut(), + winuser::RDW_INTERNALPAINT + ); + } + } + + // If new event processing has to be done (i.e. call NewEvents or defer), do it. If we're + // already in processing nothing happens with this call. + self.new_events(); + + // Now that an event has been received, we have to send any `NewEvents` calls that were + // deferred. + if let RunnerState::DeferredNewEvents(wait_start) = self.runner_state { + match self.control_flow { + ControlFlow::Wait => { + self.call_event_handler( + Event::NewEvents(StartCause::WaitCancelled { + start: wait_start, + requested_resume: None, + }) + ) + }, + ControlFlow::WaitUntil(resume_time) => { + let start_cause = match Instant::now() >= resume_time { + // If the current time is later than the requested resume time, the resume time + // has been reached. + true => StartCause::ResumeTimeReached { + start: wait_start, + requested_resume: resume_time, + }, + // Otherwise, the requested resume time HASN'T been reached and we send a WaitCancelled. + false => StartCause::WaitCancelled { + start: wait_start, + requested_resume: Some(resume_time), + }, + }; + self.call_event_handler(Event::NewEvents(start_cause)); + }, + // This can be reached if the control flow is changed to poll during a `RedrawRequested` + // that was sent after `EventsCleared`. + ControlFlow::Poll => { + self.call_event_handler(Event::NewEvents(StartCause::Poll)) + }, + ControlFlow::Exit => unreachable!() + } + } + + self.runner_state = RunnerState::HandlingEvents; + self.call_event_handler(event); + } + + fn events_cleared(&mut self) { + match self.runner_state { + // If we were handling events, send the EventsCleared message. + RunnerState::HandlingEvents => { + self.call_event_handler(Event::EventsCleared); + self.runner_state = RunnerState::Idle(Instant::now()); + }, + + // If we *weren't* handling events, we don't have to do anything. + RunnerState::New | + RunnerState::Idle(..) => (), + + // Some control flows require a NewEvents call even if no events were received. This + // branch handles those. + RunnerState::DeferredNewEvents(wait_start) => { + match self.control_flow { + // If we had deferred a Poll, send the Poll NewEvents and EventsCleared. + ControlFlow::Poll => { + self.call_event_handler(Event::NewEvents(StartCause::Poll)); + self.call_event_handler(Event::EventsCleared); + }, + // If we had deferred a WaitUntil and the resume time has since been reached, + // send the resume notification and EventsCleared event. + ControlFlow::WaitUntil(resume_time) => { + if Instant::now() >= resume_time { + self.call_event_handler(Event::NewEvents(StartCause::ResumeTimeReached { + start: wait_start, + requested_resume: resume_time, + })); + self.call_event_handler(Event::EventsCleared); + } + }, + // If we deferred a wait and no events were received, the user doesn't have to + // get an event. + ControlFlow::Wait | + ControlFlow::Exit => () + } + // Mark that we've entered an idle state. + self.runner_state = RunnerState::Idle(wait_start) + }, + } + } + + fn call_event_handler(&mut self, event: Event) { + match event { + Event::NewEvents(_) => self.trigger_newevents_on_redraw.store(true, Ordering::Relaxed), + Event::EventsCleared => self.trigger_newevents_on_redraw.store(false, Ordering::Relaxed), + _ => () + } + + + if self.panic_error.is_none() { + let EventLoopRunner { + ref mut panic_error, + ref mut event_handler, + ref mut control_flow, + .. + } = self; + *panic_error = panic::catch_unwind(panic::AssertUnwindSafe(|| { + if *control_flow != ControlFlow::Exit { + (*event_handler)(event, control_flow); + } else { + (*event_handler)(event, &mut ControlFlow::Exit); + } + })).err(); + } + } +} + +// Returns true if the wait time was reached, and false if a message must be processed. +unsafe fn wait_until_time_or_msg(wait_until: Instant) -> bool { + let mut msg = mem::uninitialized(); + let now = Instant::now(); + if now <= wait_until { + // MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract 1 millisecond + // from the requested time and spinlock for the remainder to compensate for that. + let resume_reason = winuser::MsgWaitForMultipleObjectsEx( + 0, + ptr::null(), + dur2timeout(wait_until - now).saturating_sub(1), + winuser::QS_ALLEVENTS, + winuser::MWMO_INPUTAVAILABLE + ); + + if resume_reason == winerror::WAIT_TIMEOUT { + while Instant::now() < wait_until { + if 0 != winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) { + return false; + } + } + } + } + + return true; +} +// Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs +fn dur2timeout(dur: Duration) -> DWORD { + // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the + // timeouts in windows APIs are typically u32 milliseconds. To translate, we + // have two pieces to take care of: + // + // * Nanosecond precision is rounded up + // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE + // (never time out). + dur.as_secs().checked_mul(1000).and_then(|ms| { + ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000) + }).and_then(|ms| { + ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 {1} else {0}) + }).map(|ms| { + if ms > DWORD::max_value() as u64 { + winbase::INFINITE + } else { + ms as DWORD + } + }).unwrap_or(winbase::INFINITE) +} + +impl Drop for EventLoop { + fn drop(&mut self) { + unsafe { + winuser::DestroyWindow(self.window_target.p.thread_msg_target); + } + } +} + +pub(crate) struct EventLoopThreadExecutor { + thread_id: DWORD, + trigger_newevents_on_redraw: Arc, + target_window: HWND, +} + +unsafe impl Send for EventLoopThreadExecutor {} +unsafe impl Sync for EventLoopThreadExecutor {} + +impl EventLoopThreadExecutor { + /// Check to see if we're in the parent event loop's thread. + pub(super) fn in_event_loop_thread(&self) -> bool { + let cur_thread_id = unsafe { processthreadsapi::GetCurrentThreadId() }; + self.thread_id == cur_thread_id + } + + pub(super) fn trigger_newevents_on_redraw(&self) -> bool { + !self.in_event_loop_thread() || self.trigger_newevents_on_redraw.load(Ordering::Relaxed) + } + + /// Executes a function in the event loop thread. If we're already in the event loop thread, + /// we just call the function directly. + /// + /// 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. + /// + /// Note that if you are using this to change some property of a window and updating + /// `WindowState` then you should call this within the lock of `WindowState`. Otherwise the + /// events may be sent to the other thread in different order to the one in which you set + /// `WindowState`, leaving them out of sync. + /// + /// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent + /// to the unstable FnBox. + pub(super) fn execute_in_thread(&self, mut function: F) + where F: FnMut() + Send + 'static + { + unsafe { + if self.in_event_loop_thread() { + function(); + } else { + // We double-box because the first box is a fat pointer. + let boxed = Box::new(function) as Box; + let boxed2: ThreadExecFn = Box::new(boxed); + + let raw = Box::into_raw(boxed2); + + let res = winuser::PostMessageW( + self.target_window, *EXEC_MSG_ID, + raw as *mut () as usize as WPARAM, 0 + ); + assert!(res != 0, "PostMessage failed ; is the messages queue full?"); + } + } + } +} + +type ThreadExecFn = Box>; + +#[derive(Clone)] +pub struct EventLoopProxy { + target_window: HWND, + event_send: Sender, +} +unsafe impl Send for EventLoopProxy {} + +impl EventLoopProxy { + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + unsafe { + if winuser::PostMessageW(self.target_window, *USER_EVENT_MSG_ID, 0, 0) != 0 { + self.event_send.send(event).ok(); + Ok(()) + } else { + Err(EventLoopClosed) + } + } + } +} + +lazy_static! { + // Message sent by the `EventLoopProxy` when we want to wake up the thread. + // WPARAM and LPARAM are unused. + static ref USER_EVENT_MSG_ID: u32 = { + unsafe { + winuser::RegisterWindowMessageA("Winit::WakeupMsg\0".as_ptr() as LPCSTR) + } + }; + // Message sent when we want to execute a closure in the thread. + // WPARAM contains a Box> that must be retrieved with `Box::from_raw`, + // and LPARAM is unused. + static ref EXEC_MSG_ID: u32 = { + unsafe { + winuser::RegisterWindowMessageA("Winit::ExecMsg\0".as_ptr() as *const i8) + } + }; + // Message sent by a `Window` when it wants to be destroyed by the main thread. + // WPARAM and LPARAM are unused. + pub static ref DESTROY_MSG_ID: u32 = { + unsafe { + winuser::RegisterWindowMessageA("Winit::DestroyMsg\0".as_ptr() as LPCSTR) + } + }; + // Message sent by a `Window` after creation if it has a DPI != 96. + // WPARAM is the the DPI (u32). LOWORD of LPARAM is width, and HIWORD is height. + pub static ref INITIAL_DPI_MSG_ID: u32 = { + unsafe { + winuser::RegisterWindowMessageA("Winit::InitialDpiMsg\0".as_ptr() as LPCSTR) + } + }; + // Message sent by a `Window` if it's requesting a redraw without sending a NewEvents. + pub static ref REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID: u32 = { + unsafe { + winuser::RegisterWindowMessageA("Winit::RequestRedrawNoNewevents\0".as_ptr() as LPCSTR) + } + }; + // WPARAM is a bool specifying the `WindowFlags::MARKER_RETAIN_STATE_ON_SIZE` flag. See the + // documentation in the `window_state` module for more information. + pub static ref SET_RETAIN_STATE_ON_SIZE_MSG_ID: u32 = unsafe { + winuser::RegisterWindowMessageA("Winit::SetRetainMaximized\0".as_ptr() as LPCSTR) + }; + static ref THREAD_EVENT_TARGET_WINDOW_CLASS: Vec = unsafe { + use std::ffi::OsStr; + use std::os::windows::ffi::OsStrExt; + + let class_name: Vec<_> = OsStr::new("Winit Thread Event Target") + .encode_wide() + .chain(Some(0).into_iter()) + .collect(); + + let class = winuser::WNDCLASSEXW { + cbSize: mem::size_of::() as UINT, + style: 0, + lpfnWndProc: Some(winuser::DefWindowProcW), + cbClsExtra: 0, + cbWndExtra: 0, + hInstance: libloaderapi::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(), + }; + + winuser::RegisterClassExW(&class); + + class_name + }; +} + +fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> (HWND, Sender) { + unsafe { + let window = winuser::CreateWindowExW( + winuser::WS_EX_NOACTIVATE | winuser::WS_EX_TRANSPARENT | winuser::WS_EX_LAYERED, + THREAD_EVENT_TARGET_WINDOW_CLASS.as_ptr(), + ptr::null_mut(), + 0, + 0, 0, + 0, 0, + ptr::null_mut(), + ptr::null_mut(), + libloaderapi::GetModuleHandleW(ptr::null()), + ptr::null_mut() + ); + winuser::SetWindowLongPtrW( + window, + winuser::GWL_STYLE, + // The window technically has to be visible to receive WM_PAINT messages (which are used + // for delivering events during resizes), but it isn't displayed to the user because of + // the LAYERED style. + (winuser::WS_VISIBLE | winuser::WS_POPUP) as _ + ); + + let (tx, rx) = mpsc::channel(); + + let subclass_input = ThreadMsgTargetSubclassInput { + event_loop_runner, + user_event_receiver: rx, + }; + let input_ptr = Box::into_raw(Box::new(subclass_input)); + let subclass_result = commctrl::SetWindowSubclass( + window, + Some(thread_event_target_callback::), + THREAD_EVENT_TARGET_SUBCLASS_ID, + input_ptr as DWORD_PTR + ); + assert_eq!(subclass_result, 1); + + (window, tx) + } +} + +/// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of +/// the window. +unsafe fn capture_mouse(window: HWND, window_state: &mut WindowState) { + window_state.mouse.buttons_down += 1; + winuser::SetCapture(window); +} + +/// Release mouse input, stopping windows on this thread from receiving mouse input when the cursor +/// is outside the window. +unsafe fn release_mouse(window_state: &mut WindowState) { + window_state.mouse.buttons_down = window_state.mouse.buttons_down.saturating_sub(1); + if window_state.mouse.buttons_down == 0 { + winuser::ReleaseCapture(); + } +} + +const WINDOW_SUBCLASS_ID: UINT_PTR = 0; +const THREAD_EVENT_TARGET_SUBCLASS_ID: UINT_PTR = 1; +pub(crate) fn subclass_window(window: HWND, subclass_input: SubclassInput) { + let input_ptr = Box::into_raw(Box::new(subclass_input)); + let subclass_result = unsafe{ commctrl::SetWindowSubclass( + window, + Some(public_window_callback::), + WINDOW_SUBCLASS_ID, + input_ptr as DWORD_PTR + ) }; + assert_eq!(subclass_result, 1); +} + +/// 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 +unsafe extern "system" fn public_window_callback( + window: HWND, + msg: UINT, + wparam: WPARAM, + lparam: LPARAM, + _: UINT_PTR, + subclass_input_ptr: DWORD_PTR +) -> LRESULT { + let subclass_input = &mut*(subclass_input_ptr as *mut SubclassInput); + + match msg { + winuser::WM_ENTERSIZEMOVE => { + let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + if let Some(ref mut runner) = *runner { + runner.in_modal_loop = true; + } + 0 + }, + winuser::WM_EXITSIZEMOVE => { + let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + if let Some(ref mut runner) = *runner { + runner.in_modal_loop = false; + } + 0 + }, + winuser::WM_NCCREATE => { + enable_non_client_dpi_scaling(window); + commctrl::DefSubclassProc(window, msg, wparam, lparam) + }, + + winuser::WM_CLOSE => { + use event::WindowEvent::CloseRequested; + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: CloseRequested, + }); + 0 + }, + + winuser::WM_DESTROY => { + use event::WindowEvent::Destroyed; + ole2::RevokeDragDrop(window); + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: Destroyed, + }); + + Box::from_raw(subclass_input); + drop(subclass_input); + 0 + }, + + _ if msg == *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID => { + use event::WindowEvent::RedrawRequested; + let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + if let Some(ref mut runner) = *runner { + match runner.runner_state { + RunnerState::Idle(..) | + RunnerState::DeferredNewEvents(..) => runner.call_event_handler(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: RedrawRequested, + }), + _ => () + } + } + 0 + }, + winuser::WM_PAINT => { + use event::WindowEvent::RedrawRequested; + let event = || Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: RedrawRequested, + }; + + let mut send_event = false; + { + let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + if let Some(ref mut runner) = *runner { + match runner.runner_state { + RunnerState::Idle(..) | + RunnerState::DeferredNewEvents(..) => runner.call_event_handler(event()), + _ => send_event = true + } + } + } + if send_event { + subclass_input.send_event(event()); + } + commctrl::DefSubclassProc(window, msg, wparam, lparam) + }, + + // WM_MOVE supplies client area positions, so we send Moved here instead. + winuser::WM_WINDOWPOSCHANGED => { + use event::WindowEvent::Moved; + + let windowpos = lparam as *const winuser::WINDOWPOS; + if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE { + let dpi_factor = get_hwnd_scale_factor(window); + let logical_position = LogicalPosition::from_physical( + ((*windowpos).x, (*windowpos).y), + dpi_factor, + ); + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: Moved(logical_position), + }); + } + + // This is necessary for us to still get sent WM_SIZE. + commctrl::DefSubclassProc(window, msg, wparam, lparam) + }, + + winuser::WM_SIZE => { + use event::WindowEvent::Resized; + let w = LOWORD(lparam as DWORD) as u32; + let h = HIWORD(lparam as DWORD) as u32; + + let dpi_factor = get_hwnd_scale_factor(window); + let logical_size = LogicalSize::from_physical((w, h), dpi_factor); + let event = Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: Resized(logical_size), + }; + + { + let mut w = subclass_input.window_state.lock(); + // See WindowFlags::MARKER_RETAIN_STATE_ON_SIZE docs for info on why this `if` check exists. + if !w.window_flags().contains(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE) { + let maximized = wparam == winuser::SIZE_MAXIMIZED; + w.set_window_flags_in_place(|f| f.set(WindowFlags::MAXIMIZED, maximized)); + } + } + + subclass_input.send_event(event); + 0 + }, + + winuser::WM_CHAR => { + use std::mem; + use event::WindowEvent::ReceivedCharacter; + let chr: char = mem::transmute(wparam as u32); + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(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. + winuser::WM_SYSCHAR => { + 0 + } + + winuser::WM_MOUSEMOVE => { + use event::WindowEvent::{CursorEntered, CursorMoved}; + let mouse_was_outside_window = { + let mut w = subclass_input.window_state.lock(); + + let was_outside_window = !w.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW); + w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, true)).ok(); + was_outside_window + }; + + if mouse_was_outside_window { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: CursorEntered { device_id: DEVICE_ID }, + }); + + // Calling TrackMouseEvent in order to receive mouse leave events. + winuser::TrackMouseEvent(&mut winuser::TRACKMOUSEEVENT { + cbSize: mem::size_of::() as DWORD, + dwFlags: winuser::TME_LEAVE, + hwndTrack: window, + dwHoverTime: winuser::HOVER_DEFAULT, + }); + } + + let x = windowsx::GET_X_LPARAM(lparam) as f64; + let y = windowsx::GET_Y_LPARAM(lparam) as f64; + let dpi_factor = get_hwnd_scale_factor(window); + let position = LogicalPosition::from_physical((x, y), dpi_factor); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() }, + }); + + 0 + }, + + winuser::WM_MOUSELEAVE => { + use event::WindowEvent::CursorLeft; + { + let mut w = subclass_input.window_state.lock(); + w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)).ok(); + } + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: CursorLeft { device_id: DEVICE_ID }, + }); + + 0 + }, + + winuser::WM_MOUSEWHEEL => { + use event::MouseScrollDelta::LineDelta; + use event::TouchPhase; + + let value = (wparam >> 16) as i16; + let value = value as i32; + let value = value as f32 / winuser::WHEEL_DELTA as f32; + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() }, + }); + + 0 + }, + + winuser::WM_KEYDOWN | winuser::WM_SYSKEYDOWN => { + use event::ElementState::Pressed; + use event::VirtualKeyCode; + if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { + commctrl::DefSubclassProc(window, msg, wparam, lparam) + } else { + if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + state: Pressed, + scancode: scancode, + virtual_keycode: vkey, + modifiers: event::get_key_mods(), + }, + }, + }); + // Windows doesn't emit a delete character by default, but in order to make it + // consistent with the other platforms we'll emit a delete character here. + if vkey == Some(VirtualKeyCode::Delete) { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::ReceivedCharacter('\u{7F}'), + }); + } + } + 0 + } + }, + + winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { + use event::ElementState::Released; + if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::KeyboardInput { + device_id: DEVICE_ID, + input: KeyboardInput { + state: Released, + scancode: scancode, + virtual_keycode: vkey, + modifiers: event::get_key_mods(), + }, + }, + }); + } + 0 + }, + + winuser::WM_LBUTTONDOWN => { + use event::WindowEvent::MouseInput; + use event::MouseButton::Left; + use event::ElementState::Pressed; + + capture_mouse(window, &mut *subclass_input.window_state.lock()); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left, modifiers: event::get_key_mods() }, + }); + 0 + }, + + winuser::WM_LBUTTONUP => { + use event::WindowEvent::MouseInput; + use event::MouseButton::Left; + use event::ElementState::Released; + + release_mouse(&mut *subclass_input.window_state.lock()); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left, modifiers: event::get_key_mods() }, + }); + 0 + }, + + winuser::WM_RBUTTONDOWN => { + use event::WindowEvent::MouseInput; + use event::MouseButton::Right; + use event::ElementState::Pressed; + + capture_mouse(window, &mut *subclass_input.window_state.lock()); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right, modifiers: event::get_key_mods() }, + }); + 0 + }, + + winuser::WM_RBUTTONUP => { + use event::WindowEvent::MouseInput; + use event::MouseButton::Right; + use event::ElementState::Released; + + release_mouse(&mut *subclass_input.window_state.lock()); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right, modifiers: event::get_key_mods() }, + }); + 0 + }, + + winuser::WM_MBUTTONDOWN => { + use event::WindowEvent::MouseInput; + use event::MouseButton::Middle; + use event::ElementState::Pressed; + + capture_mouse(window, &mut *subclass_input.window_state.lock()); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle, modifiers: event::get_key_mods() }, + }); + 0 + }, + + winuser::WM_MBUTTONUP => { + use event::WindowEvent::MouseInput; + use event::MouseButton::Middle; + use event::ElementState::Released; + + release_mouse(&mut *subclass_input.window_state.lock()); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle, modifiers: event::get_key_mods() }, + }); + 0 + }, + + winuser::WM_XBUTTONDOWN => { + use event::WindowEvent::MouseInput; + use event::MouseButton::Other; + use event::ElementState::Pressed; + let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); + + capture_mouse(window, &mut *subclass_input.window_state.lock()); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8), modifiers: event::get_key_mods() }, + }); + 0 + }, + + winuser::WM_XBUTTONUP => { + use event::WindowEvent::MouseInput; + use event::MouseButton::Other; + use event::ElementState::Released; + let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); + + release_mouse(&mut *subclass_input.window_state.lock()); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8), modifiers: event::get_key_mods() }, + }); + 0 + }, + + winuser::WM_INPUT_DEVICE_CHANGE => { + let event = match wparam as _ { + winuser::GIDC_ARRIVAL => DeviceEvent::Added, + winuser::GIDC_REMOVAL => DeviceEvent::Removed, + _ => unreachable!(), + }; + + subclass_input.send_event(Event::DeviceEvent { + device_id: wrap_device_id(lparam as _), + event, + }); + + 0 + }, + + winuser::WM_INPUT => { + use event::DeviceEvent::{Motion, MouseMotion, MouseWheel, Button, Key}; + use event::MouseScrollDelta::LineDelta; + use event::ElementState::{Pressed, Released}; + + if let Some(data) = get_raw_input_data(lparam as _) { + let device_id = wrap_device_id(data.header.hDevice as _); + + if data.header.dwType == winuser::RIM_TYPEMOUSE { + let mouse = data.data.mouse(); + + if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { + let x = mouse.lLastX as f64; + let y = mouse.lLastY as f64; + + if x != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Motion { axis: 0, value: x }, + }); + } + + if y != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Motion { axis: 1, value: y }, + }); + } + + if x != 0.0 || y != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: MouseMotion { delta: (x, y) }, + }); + } + } + + if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { + let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: MouseWheel { delta: LineDelta(0.0, delta as f32) }, + }); + } + + let button_state = get_raw_mouse_button_state(mouse.usButtonFlags); + // Left, middle, and right, respectively. + for (index, state) in button_state.iter().enumerate() { + if let Some(state) = *state { + // This gives us consistency with X11, since there doesn't + // seem to be anything else reasonable to do for a mouse + // button ID. + let button = (index + 1) as _; + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Button { + button, + state, + }, + }); + } + } + } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { + let keyboard = data.data.keyboard(); + + let pressed = keyboard.Message == winuser::WM_KEYDOWN + || keyboard.Message == winuser::WM_SYSKEYDOWN; + let released = keyboard.Message == winuser::WM_KEYUP + || keyboard.Message == winuser::WM_SYSKEYUP; + + if pressed || released { + let state = if pressed { + Pressed + } else { + Released + }; + + let scancode = keyboard.MakeCode as _; + let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) + | util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _); + if let Some((vkey, scancode)) = handle_extended_keys( + keyboard.VKey as _, + scancode, + extended, + ) { + let virtual_keycode = vkey_to_winit_vkey(vkey); + + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Key(KeyboardInput { + scancode, + state, + virtual_keycode, + modifiers: event::get_key_mods(), + }), + }); + } + } + } + } + + commctrl::DefSubclassProc(window, msg, wparam, lparam) + }, + + winuser::WM_TOUCH => { + let pcount = LOWORD( wparam as DWORD ) as usize; + let mut inputs = Vec::with_capacity( pcount ); + inputs.set_len( pcount ); + let htouch = lparam as winuser::HTOUCHINPUT; + if winuser::GetTouchInputInfo( + htouch, + pcount as UINT, + inputs.as_mut_ptr(), + mem::size_of::() as INT, + ) > 0 { + let dpi_factor = get_hwnd_scale_factor(window); + for input in &inputs { + let x = (input.x as f64) / 100f64; + let y = (input.y as f64) / 100f64; + let location = LogicalPosition::from_physical((x, y), dpi_factor); + subclass_input.send_event( Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: WindowEvent::Touch(Touch { + phase: + if input.dwFlags & winuser::TOUCHEVENTF_DOWN != 0 { + TouchPhase::Started + } else if input.dwFlags & winuser::TOUCHEVENTF_UP != 0 { + TouchPhase::Ended + } else if input.dwFlags & winuser::TOUCHEVENTF_MOVE != 0 { + TouchPhase::Moved + } else { + continue; + }, + location, + id: input.dwID as u64, + device_id: DEVICE_ID, + }), + }); + } + } + winuser::CloseTouchInputHandle( htouch ); + 0 + } + + winuser::WM_SETFOCUS => { + use event::WindowEvent::{Focused, CursorMoved}; + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: Focused(true), + }); + + let x = windowsx::GET_X_LPARAM(lparam) as f64; + let y = windowsx::GET_Y_LPARAM(lparam) as f64; + let dpi_factor = get_hwnd_scale_factor(window); + let position = LogicalPosition::from_physical((x, y), dpi_factor); + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() }, + }); + + 0 + }, + + winuser::WM_KILLFOCUS => { + use event::WindowEvent::Focused; + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: Focused(false), + }); + 0 + }, + + winuser::WM_SETCURSOR => { + let set_cursor_to = { + let window_state = subclass_input.window_state.lock(); + if window_state.mouse.cursor_flags().contains(CursorFlags::IN_WINDOW) { + Some(window_state.mouse.cursor) + } else { + None + } + }; + + match set_cursor_to { + Some(cursor) => { + let cursor = winuser::LoadCursorW( + ptr::null_mut(), + cursor.to_windows_cursor(), + ); + winuser::SetCursor(cursor); + 0 + }, + None => winuser::DefWindowProcW(window, msg, wparam, lparam) + } + }, + + winuser::WM_DROPFILES => { + // See `FileDropHandler` for implementation. + 0 + }, + + winuser::WM_GETMINMAXINFO => { + let mmi = lparam as *mut winuser::MINMAXINFO; + + let window_state = subclass_input.window_state.lock(); + + if window_state.min_size.is_some() || window_state.max_size.is_some() { + let style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD; + let ex_style = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD; + if let Some(min_size) = window_state.min_size { + let min_size = min_size.to_physical(window_state.dpi_factor); + let (width, height) = adjust_size(min_size, style, ex_style); + (*mmi).ptMinTrackSize = POINT { x: width as i32, y: height as i32 }; + } + if let Some(max_size) = window_state.max_size { + let max_size = max_size.to_physical(window_state.dpi_factor); + let (width, height) = adjust_size(max_size, style, ex_style); + (*mmi).ptMaxTrackSize = POINT { x: width as i32, y: height as i32 }; + } + } + + 0 + }, + + // Only sent on Windows 8.1 or newer. On Windows 7 and older user has to log out to change + // DPI, therefore all applications are closed while DPI is changing. + winuser::WM_DPICHANGED => { + use event::WindowEvent::HiDpiFactorChanged; + + // This message actually provides two DPI values - x and y. However MSDN says that + // "you only need to use either the X-axis or the Y-axis value when scaling your + // application since they are the same". + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx + let new_dpi_x = u32::from(LOWORD(wparam as DWORD)); + let new_dpi_factor = dpi_to_scale_factor(new_dpi_x); + + let allow_resize = { + let mut window_state = subclass_input.window_state.lock(); + let old_dpi_factor = window_state.dpi_factor; + window_state.dpi_factor = new_dpi_factor; + + new_dpi_factor != old_dpi_factor && window_state.fullscreen.is_none() + }; + + // This prevents us from re-applying DPI adjustment to the restored size after exiting + // fullscreen (the restored size is already DPI adjusted). + if allow_resize { + // Resize window to the size suggested by Windows. + let rect = &*(lparam as *const RECT); + winuser::SetWindowPos( + window, + ptr::null_mut(), + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + winuser::SWP_NOZORDER | winuser::SWP_NOACTIVATE, + ); + } + + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: HiDpiFactorChanged(new_dpi_factor), + }); + + 0 + }, + + _ => { + if msg == *DESTROY_MSG_ID { + winuser::DestroyWindow(window); + 0 + } else if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID { + let mut window_state = subclass_input.window_state.lock(); + window_state.set_window_flags_in_place(|f| f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0)); + 0 + } else if msg == *INITIAL_DPI_MSG_ID { + use event::WindowEvent::HiDpiFactorChanged; + let scale_factor = dpi_to_scale_factor(wparam as u32); + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: HiDpiFactorChanged(scale_factor), + }); + // Automatically resize for actual DPI + let width = LOWORD(lparam as DWORD) as u32; + let height = HIWORD(lparam as DWORD) as u32; + let (adjusted_width, adjusted_height): (u32, u32) = PhysicalSize::from_logical( + (width, height), + scale_factor, + ).into(); + // We're not done yet! `SetWindowPos` needs the window size, not the client area size. + let mut rect = RECT { + top: 0, + left: 0, + bottom: adjusted_height as LONG, + right: adjusted_width as LONG, + }; + let dw_style = winuser::GetWindowLongA(window, winuser::GWL_STYLE) as DWORD; + let b_menu = !winuser::GetMenu(window).is_null() as BOOL; + let dw_style_ex = winuser::GetWindowLongA(window, winuser::GWL_EXSTYLE) as DWORD; + winuser::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; + winuser::SetWindowPos( + window, + ptr::null_mut(), + 0, + 0, + outer_x, + outer_y, + winuser::SWP_NOMOVE + | winuser::SWP_NOREPOSITION + | winuser::SWP_NOZORDER + | winuser::SWP_NOACTIVATE, + ); + 0 + } else { + commctrl::DefSubclassProc(window, msg, wparam, lparam) + } + } + } +} + +unsafe extern "system" fn thread_event_target_callback( + window: HWND, + msg: UINT, + wparam: WPARAM, + lparam: LPARAM, + _: UINT_PTR, + subclass_input_ptr: DWORD_PTR +) -> LRESULT { + let subclass_input = &mut*(subclass_input_ptr as *mut ThreadMsgTargetSubclassInput); + match msg { + winuser::WM_DESTROY => { + Box::from_raw(subclass_input); + drop(subclass_input); + 0 + }, + // Because WM_PAINT comes after all other messages, we use it during modal loops to detect + // when the event queue has been emptied. See `process_event` for more details. + winuser::WM_PAINT => { + winuser::ValidateRect(window, ptr::null()); + let queue_call_again = || { + winuser::RedrawWindow( + window, + ptr::null(), + ptr::null_mut(), + winuser::RDW_INTERNALPAINT + ); + }; + let in_modal_loop = { + let runner = subclass_input.event_loop_runner.runner.borrow_mut(); + if let Some(ref runner) = *runner { + runner.in_modal_loop + } else { + false + } + }; + if in_modal_loop { + let mut msg = mem::uninitialized(); + loop { + if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) { + break; + } + // Clear all paint/timer messages from the queue before sending the events cleared message. + match msg.message { + // Flush the event queue of WM_PAINT messages. + winuser::WM_PAINT | + winuser::WM_TIMER => { + // Remove the message from the message queue. + winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 1); + + if msg.hwnd != window { + winuser::TranslateMessage(&mut msg); + winuser::DispatchMessageW(&mut msg); + } + }, + // If the message isn't one of those three, it may be handled by the modal + // loop so we should return control flow to it. + _ => { + queue_call_again(); + return 0; + } + } + } + + let mut runner = subclass_input.event_loop_runner.runner.borrow_mut(); + if let Some(ref mut runner) = *runner { + runner.events_cleared(); + match runner.control_flow { + // Waiting is handled by the modal loop. + ControlFlow::Exit | + ControlFlow::Wait => runner.new_events(), + ControlFlow::WaitUntil(resume_time) => { + wait_until_time_or_msg(resume_time); + runner.new_events(); + queue_call_again(); + }, + ControlFlow::Poll => { + runner.new_events(); + queue_call_again(); + } + } + } + } + 0 + } + _ if msg == *USER_EVENT_MSG_ID => { + if let Ok(event) = subclass_input.user_event_receiver.recv() { + subclass_input.send_event(Event::UserEvent(event)); + } + 0 + } + _ if msg == *EXEC_MSG_ID => { + let mut function: ThreadExecFn = Box::from_raw(wparam as usize as *mut _); + function(); + 0 + } + _ => commctrl::DefSubclassProc(window, msg, wparam, lparam) + } +} diff --git a/src/platform/windows/icon.rs b/src/platform_impl/windows/icon.rs similarity index 97% rename from src/platform/windows/icon.rs rename to src/platform_impl/windows/icon.rs index 6ea1e9906d1..c45bdf6fdee 100644 --- a/src/platform/windows/icon.rs +++ b/src/platform_impl/windows/icon.rs @@ -7,8 +7,8 @@ use winapi::shared::minwindef::{BYTE, LPARAM, WPARAM}; use winapi::shared::windef::{HICON, HWND}; use winapi::um::winuser; -use {Pixel, PIXEL_SIZE, Icon}; -use platform::platform::util; +use icon::{Pixel, PIXEL_SIZE, Icon}; +use platform_impl::platform::util; impl Pixel { fn to_bgra(&mut self) { diff --git a/src/platform/windows/mod.rs b/src/platform_impl/windows/mod.rs similarity index 80% rename from src/platform/windows/mod.rs rename to src/platform_impl/windows/mod.rs index 5ea76b7bd24..855dd6675b5 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -3,14 +3,17 @@ use winapi; use winapi::shared::windef::HWND; -pub use self::events_loop::{EventsLoop, EventsLoopProxy}; -pub use self::monitor::MonitorId; +pub use self::event_loop::{EventLoop, EventLoopWindowTarget, EventLoopProxy}; +pub use self::monitor::MonitorHandle; pub use self::window::Window; +use window::Icon; +use event::DeviceId as RootDeviceId; + #[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes { pub parent: Option, - pub taskbar_icon: Option<::Icon>, + pub taskbar_icon: Option, pub no_redirection_bitmap: bool, } @@ -43,10 +46,10 @@ impl DeviceId { } // Constant device ID, to be removed when this backend is updated to report real device IDs. -const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId(0)); +const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId(0)); -fn wrap_device_id(id: u32) -> ::DeviceId { - ::DeviceId(DeviceId(id)) +fn wrap_device_id(id: u32) -> RootDeviceId { + RootDeviceId(DeviceId(id)) } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -65,7 +68,7 @@ impl WindowId { mod dpi; mod drop_handler; mod event; -mod events_loop; +mod event_loop; mod icon; mod monitor; mod raw_input; diff --git a/src/platform/windows/monitor.rs b/src/platform_impl/windows/monitor.rs similarity index 78% rename from src/platform/windows/monitor.rs rename to src/platform_impl/windows/monitor.rs index d7080ca27f5..585a2e0d374 100644 --- a/src/platform/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -6,14 +6,14 @@ use winapi::um::winuser; use std::{mem, ptr}; use std::collections::VecDeque; -use super::{EventsLoop, util}; +use super::{EventLoop, util}; use dpi::{PhysicalPosition, PhysicalSize}; -use platform::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi}; -use platform::platform::window::Window; +use platform_impl::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi}; +use platform_impl::platform::window::Window; -/// Win32 implementation of the main `MonitorId` object. +/// Win32 implementation of the main `MonitorHandle` object. #[derive(Debug, Clone)] -pub struct MonitorId { +pub struct MonitorHandle { /// Monitor handle. hmonitor: HMonitor, /// The system name of the monitor. @@ -45,13 +45,13 @@ unsafe extern "system" fn monitor_enum_proc( _place: LPRECT, data: LPARAM, ) -> BOOL { - let monitors = data as *mut VecDeque; - (*monitors).push_back(MonitorId::from_hmonitor(hmonitor)); + let monitors = data as *mut VecDeque; + (*monitors).push_back(MonitorHandle::from_hmonitor(hmonitor)); TRUE // continue enumeration } -pub fn get_available_monitors() -> VecDeque { - let mut monitors: VecDeque = VecDeque::new(); +pub fn get_available_monitors() -> VecDeque { + let mut monitors: VecDeque = VecDeque::new(); unsafe { winuser::EnumDisplayMonitors( ptr::null_mut(), @@ -63,38 +63,38 @@ pub fn get_available_monitors() -> VecDeque { monitors } -pub fn get_primary_monitor() -> MonitorId { +pub fn get_primary_monitor() -> MonitorHandle { const ORIGIN: POINT = POINT { x: 0, y: 0 }; let hmonitor = unsafe { winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY) }; - MonitorId::from_hmonitor(hmonitor) + MonitorHandle::from_hmonitor(hmonitor) } -impl EventsLoop { +pub fn get_current_monitor(hwnd: HWND) -> MonitorHandle { + let hmonitor = unsafe { + winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST) + }; + MonitorHandle::from_hmonitor(hmonitor) +} + +impl EventLoop { // TODO: Investigate opportunities for caching - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { 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) -> MonitorHandle { get_primary_monitor() } } impl Window { - pub fn get_available_monitors(&self) -> VecDeque { + pub fn get_available_monitors(&self) -> VecDeque { get_available_monitors() } - pub fn get_primary_monitor(&self) -> MonitorId { + pub fn get_primary_monitor(&self) -> MonitorHandle { get_primary_monitor() } } @@ -115,7 +115,7 @@ pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result Self { let monitor_info = get_monitor_info(hmonitor).expect("`GetMonitorInfoW` failed"); let place = monitor_info.rcMonitor; @@ -123,7 +123,7 @@ impl MonitorId { (place.right - place.left) as u32, (place.bottom - place.top) as u32, ); - MonitorId { + MonitorHandle { hmonitor: HMonitor(hmonitor), monitor_name: util::wchar_ptr_to_string(monitor_info.szDevice.as_ptr()), primary: util::has_flag(monitor_info.dwFlags, winuser::MONITORINFOF_PRIMARY), diff --git a/src/platform/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs similarity index 99% rename from src/platform/windows/raw_input.rs rename to src/platform_impl/windows/raw_input.rs index 2f6e90594f6..03c26d11d99 100644 --- a/src/platform/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -31,8 +31,8 @@ use winapi::um::winuser::{ RID_INPUT, }; -use platform::platform::util; -use events::ElementState; +use platform_impl::platform::util; +use event::ElementState; #[allow(dead_code)] pub fn get_raw_input_device_list() -> Option> { diff --git a/src/platform/windows/util.rs b/src/platform_impl/windows/util.rs similarity index 99% rename from src/platform/windows/util.rs rename to src/platform_impl/windows/util.rs index 0f7e3887c34..cf20d3c7326 100644 --- a/src/platform/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -2,7 +2,7 @@ use std::{self, mem, ptr, slice, io}; use std::ops::BitAnd; use std::sync::atomic::{AtomicBool, Ordering}; -use MouseCursor; +use window::MouseCursor; use winapi::ctypes::wchar_t; use winapi::shared::minwindef::{BOOL, DWORD}; use winapi::shared::windef::{HWND, POINT, RECT}; diff --git a/src/platform/windows/window.rs b/src/platform_impl/windows/window.rs similarity index 83% rename from src/platform/windows/window.rs rename to src/platform_impl/windows/window.rs index 0d664d4d5aa..7fe74c32984 100644 --- a/src/platform/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -4,36 +4,34 @@ use std::{io, mem, ptr}; use std::cell::Cell; use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::sync::mpsc::channel; +use parking_lot::Mutex; use winapi::ctypes::c_int; use winapi::shared::minwindef::{DWORD, LPARAM, UINT, WORD, WPARAM}; use winapi::shared::windef::{HWND, POINT, RECT}; -use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser}; -use winapi::um::objbase::COINIT_MULTITHREADED; +use winapi::um::{combaseapi, dwmapi, libloaderapi, ole2, winuser}; +use winapi::um::objbase::COINIT_APARTMENTTHREADED; use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2}; use winapi::um::wingdi::{CreateRectRgn, DeleteObject}; +use winapi::um::oleidl::LPDROPTARGET; use winapi::um::winnt::{LONG, LPCWSTR}; -use { - CreationError, - Icon, - LogicalPosition, - LogicalSize, - MonitorId as RootMonitorId, - MouseCursor, - PhysicalSize, - WindowAttributes, +use window::{CreationError, Icon, MouseCursor, WindowAttributes}; +use dpi::{LogicalPosition, LogicalSize, PhysicalSize}; +use monitor::MonitorHandle as RootMonitorHandle; +use platform_impl::platform::{ + {PlatformSpecificWindowBuilderAttributes, WindowId}, + dpi::{dpi_to_scale_factor, get_hwnd_dpi}, + drop_handler::FileDropHandler, + event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID, REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID}, + icon::{self, IconType, WinIcon}, + monitor, + raw_input::register_all_mice_and_keyboards_for_raw_input, + util, + window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}, }; -use platform::platform::{PlatformSpecificWindowBuilderAttributes, WindowId}; -use platform::platform::dpi::{dpi_to_scale_factor, get_hwnd_dpi}; -use platform::platform::events_loop::{self, EventsLoop, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID}; -use platform::platform::icon::{self, IconType, WinIcon}; -use platform::platform::monitor::get_available_monitors; -use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input; -use platform::platform::util; -use platform::platform::window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}; /// The Win32 implementation of the main `Window` object. pub struct Window { @@ -44,24 +42,54 @@ pub struct Window { window_state: Arc>, // The events loop proxy. - events_loop_proxy: events_loop::EventsLoopProxy, + thread_executor: event_loop::EventLoopThreadExecutor, } impl Window { - pub fn new( - events_loop: &EventsLoop, + pub fn new( + event_loop: &EventLoopWindowTarget, w_attr: WindowAttributes, pl_attr: PlatformSpecificWindowBuilderAttributes, ) -> Result { - let (tx, rx) = channel(); - let proxy = events_loop.create_proxy(); - events_loop.execute_in_thread(move |inserter| { - // We dispatch an `init` function because of code style. - // First person to remove the need for cloning here gets a cookie! - let win = unsafe { init(w_attr.clone(), pl_attr.clone(), inserter, proxy.clone()) }; - let _ = tx.send(win); - }); - rx.recv().unwrap() + // We dispatch an `init` function because of code style. + // First person to remove the need for cloning here gets a cookie! + // + // done. you owe me -- ossi + unsafe { + init(w_attr, pl_attr, event_loop).map(|win| { + let file_drop_handler = { + use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE, S_OK}; + + let ole_init_result = ole2::OleInitialize(ptr::null_mut()); + // It is ok if the initialize result is `S_FALSE` because it might happen that + // multiple windows are created on the same thread. + if ole_init_result == OLE_E_WRONGCOMPOBJ { + panic!("OleInitialize failed! Result was: `OLE_E_WRONGCOMPOBJ`"); + } else if ole_init_result == RPC_E_CHANGED_MODE { + panic!("OleInitialize failed! Result was: `RPC_E_CHANGED_MODE`"); + } + + let file_drop_runner = event_loop.runner_shared.clone(); + let file_drop_handler = FileDropHandler::new( + win.window.0, + Box::new(move |event| if let Ok(e) = event.map_nonuser_event() {file_drop_runner.send_event(e)}) + ); + let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET; + + assert_eq!(ole2::RegisterDragDrop(win.window.0, handler_interface_ptr), S_OK); + file_drop_handler + }; + + let subclass_input = event_loop::SubclassInput { + window_state: win.window_state.clone(), + event_loop_runner: event_loop.runner_shared.clone(), + file_drop_handler, + }; + + event_loop::subclass_window(win.window.0, subclass_input); + win + }) + } } pub fn set_title(&self, text: &str) { @@ -88,6 +116,22 @@ impl Window { } } + #[inline] + pub fn request_redraw(&self) { + unsafe { + if self.thread_executor.trigger_newevents_on_redraw() { + winuser::RedrawWindow( + self.window.0, + ptr::null(), + ptr::null_mut(), + winuser::RDW_INTERNALPAINT + ); + } else { + winuser::PostMessageW(self.window.0, *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID, 0, 0); + } + } + } + pub(crate) fn get_position_physical(&self) -> Option<(i32, i32)> { util::get_window_rect(self.window.0) .map(|rect| (rect.left as i32, rect.top as i32)) @@ -216,7 +260,7 @@ impl Window { } pub(crate) fn set_min_dimensions_physical(&self, dimensions: Option<(u32, u32)>) { - self.window_state.lock().unwrap().min_size = dimensions.map(Into::into); + self.window_state.lock().min_size = dimensions.map(Into::into); // Make windows re-check the window size bounds. self.get_inner_size_physical() .map(|(width, height)| self.set_inner_size_physical(width, height)); @@ -232,7 +276,7 @@ impl Window { } pub fn set_max_dimensions_physical(&self, dimensions: Option<(u32, u32)>) { - self.window_state.lock().unwrap().max_size = dimensions.map(Into::into); + self.window_state.lock().max_size = dimensions.map(Into::into); // Make windows re-check the window size bounds. self.get_inner_size_physical() .map(|(width, height)| self.set_inner_size_physical(width, height)); @@ -252,9 +296,9 @@ impl Window { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); - self.events_loop_proxy.execute_in_thread(move |_| { + self.thread_executor.execute_in_thread(move || { WindowState::set_window_flags( - window_state.lock().unwrap(), + window_state.lock(), window.0, None, |f| f.set(WindowFlags::RESIZABLE, resizable), @@ -270,8 +314,8 @@ impl Window { #[inline] pub fn set_cursor(&self, cursor: MouseCursor) { - self.window_state.lock().unwrap().mouse.cursor = cursor; - self.events_loop_proxy.execute_in_thread(move |_| unsafe { + self.window_state.lock().mouse.cursor = cursor; + self.thread_executor.execute_in_thread(move || unsafe { let cursor = winuser::LoadCursorW( ptr::null_mut(), cursor.to_windows_cursor(), @@ -286,8 +330,8 @@ impl Window { let window_state = Arc::clone(&self.window_state); let (tx, rx) = channel(); - self.events_loop_proxy.execute_in_thread(move |_| { - let result = window_state.lock().unwrap().mouse + self.thread_executor.execute_in_thread(move || { + let result = window_state.lock().mouse .set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab)) .map_err(|e| e.to_string()); let _ = tx.send(result); @@ -301,8 +345,8 @@ impl Window { let window_state = Arc::clone(&self.window_state); let (tx, rx) = channel(); - self.events_loop_proxy.execute_in_thread(move |_| { - let result = window_state.lock().unwrap().mouse + self.thread_executor.execute_in_thread(move || { + let result = window_state.lock().mouse .set_cursor_flags(window.0, |f| f.set(CursorFlags::HIDDEN, hide)) .map_err(|e| e.to_string()); let _ = tx.send(result); @@ -312,7 +356,7 @@ impl Window { #[inline] pub fn get_hidpi_factor(&self) -> f64 { - self.window_state.lock().unwrap().dpi_factor + self.window_state.lock().dpi_factor } fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), String> { @@ -345,9 +389,9 @@ impl Window { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); - self.events_loop_proxy.execute_in_thread(move |_| { + self.thread_executor.execute_in_thread(move || { WindowState::set_window_flags( - window_state.lock().unwrap(), + window_state.lock(), window.0, None, |f| f.set(WindowFlags::MAXIMIZED, maximized), @@ -356,19 +400,19 @@ impl Window { } #[inline] - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, monitor: Option) { unsafe { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); match &monitor { - &Some(RootMonitorId { ref inner }) => { + &Some(RootMonitorHandle { ref inner }) => { let (x, y): (i32, i32) = inner.get_position().into(); let (width, height): (u32, u32) = inner.get_dimensions().into(); let mut monitor = monitor.clone(); - self.events_loop_proxy.execute_in_thread(move |_| { - let mut window_state_lock = window_state.lock().unwrap(); + self.thread_executor.execute_in_thread(move || { + let mut window_state_lock = window_state.lock(); let client_rect = util::get_client_rect(window.0).expect("get client rect failed!"); window_state_lock.saved_window = Some(SavedWindow { @@ -392,8 +436,8 @@ impl Window { }); } &None => { - self.events_loop_proxy.execute_in_thread(move |_| { - let mut window_state_lock = window_state.lock().unwrap(); + self.thread_executor.execute_in_thread(move || { + let mut window_state_lock = window_state.lock(); window_state_lock.fullscreen = None; if let Some(SavedWindow{client_rect, dpi_factor}) = window_state_lock.saved_window { @@ -419,10 +463,10 @@ impl Window { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); - self.events_loop_proxy.execute_in_thread(move |_| { + self.thread_executor.execute_in_thread(move || { let client_rect = util::get_client_rect(window.0).expect("get client rect failed!"); WindowState::set_window_flags( - window_state.lock().unwrap(), + window_state.lock(), window.0, Some(client_rect), |f| f.set(WindowFlags::DECORATIONS, decorations), @@ -435,9 +479,9 @@ impl Window { let window = self.window.clone(); let window_state = Arc::clone(&self.window_state); - self.events_loop_proxy.execute_in_thread(move |_| { + self.thread_executor.execute_in_thread(move || { WindowState::set_window_flags( - window_state.lock().unwrap(), + window_state.lock(), window.0, None, |f| f.set(WindowFlags::ALWAYS_ON_TOP, always_on_top), @@ -446,9 +490,9 @@ impl Window { } #[inline] - pub fn get_current_monitor(&self) -> RootMonitorId { - RootMonitorId { - inner: EventsLoop::get_current_monitor(self.window.0), + pub fn get_current_monitor(&self) -> RootMonitorHandle { + RootMonitorHandle { + inner: monitor::get_current_monitor(self.window.0), } } @@ -462,7 +506,7 @@ impl Window { } else { icon::unset_for_window(self.window.0, IconType::Small); } - self.window_state.lock().unwrap().window_icon = window_icon; + self.window_state.lock().window_icon = window_icon; } #[inline] @@ -475,7 +519,7 @@ impl Window { } else { icon::unset_for_window(self.window.0, IconType::Big); } - self.window_state.lock().unwrap().taskbar_icon = taskbar_icon; + self.window_state.lock().taskbar_icon = taskbar_icon; } #[inline] @@ -523,11 +567,10 @@ pub unsafe fn adjust_size( (rect.right - rect.left, rect.bottom - rect.top) } -unsafe fn init( +unsafe fn init( mut attributes: WindowAttributes, mut pl_attribs: PlatformSpecificWindowBuilderAttributes, - inserter: events_loop::Inserter, - events_loop_proxy: events_loop::EventsLoopProxy, + event_loop: &EventLoopWindowTarget, ) -> Result { let title = OsStr::new(&attributes.title) .encode_wide() @@ -563,7 +606,7 @@ unsafe fn init( let class_name = register_window_class(&window_icon, &taskbar_icon); let guessed_dpi_factor = { - let monitors = get_available_monitors(); + let monitors = monitor::get_available_monitors(); let dpi_factor = if !monitors.is_empty() { let mut dpi_factor = Some(monitors[0].get_hidpi_factor()); for monitor in &monitors { @@ -695,7 +738,7 @@ unsafe fn init( ); let window_state = Arc::new(Mutex::new(window_state)); WindowState::set_window_flags( - window_state.lock().unwrap(), + window_state.lock(), real_window.0, None, |f| *f = window_flags, @@ -706,7 +749,7 @@ unsafe fn init( let win = Window { window: real_window, window_state, - events_loop_proxy, + thread_executor: event_loop.create_thread_executor(), }; if let Some(_) = attributes.fullscreen { @@ -718,8 +761,6 @@ unsafe fn init( win.set_inner_size(dimensions); } - inserter.insert(win.window.0, win.window_state.clone()); - Ok(win) } @@ -744,7 +785,7 @@ unsafe fn register_window_class( let class = winuser::WNDCLASSEXW { cbSize: mem::size_of::() as UINT, style: winuser::CS_HREDRAW | winuser::CS_VREDRAW | winuser::CS_OWNDC, - lpfnWndProc: Some(events_loop::callback), + lpfnWndProc: Some(winuser::DefWindowProcW), cbClsExtra: 0, cbWndExtra: 0, hInstance: libloaderapi::GetModuleHandleW(ptr::null()), @@ -775,7 +816,7 @@ impl Drop for ComInitialized { thread_local!{ static COM_INITIALIZED: ComInitialized = { unsafe { - combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED); + combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_APARTMENTTHREADED); ComInitialized(ptr::null_mut()) } }; diff --git a/src/platform/windows/window_state.rs b/src/platform_impl/windows/window_state.rs similarity index 95% rename from src/platform/windows/window_state.rs rename to src/platform_impl/windows/window_state.rs index fca044544cc..844707d6f81 100644 --- a/src/platform/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -1,9 +1,10 @@ -use {MouseCursor, WindowAttributes}; +use monitor::MonitorHandle; +use window::{MouseCursor, WindowAttributes}; use std::{io, ptr}; -use std::sync::MutexGuard; +use parking_lot::MutexGuard; use dpi::LogicalSize; -use platform::platform::{util, events_loop}; -use platform::platform::icon::WinIcon; +use platform_impl::platform::{util, event_loop}; +use platform_impl::platform::icon::WinIcon; use winapi::shared::windef::{RECT, HWND}; use winapi::shared::minwindef::DWORD; use winapi::um::winuser; @@ -23,7 +24,7 @@ pub struct WindowState { pub saved_window: Option, pub dpi_factor: f64, - pub fullscreen: Option<::MonitorId>, + pub fullscreen: Option, window_flags: WindowFlags, } @@ -36,6 +37,7 @@ pub struct SavedWindow { #[derive(Clone)] pub struct MouseProperties { pub cursor: MouseCursor, + pub buttons_down: u32, cursor_flags: CursorFlags, } @@ -89,6 +91,7 @@ impl WindowState { WindowState { mouse: MouseProperties { cursor: MouseCursor::default(), + buttons_down: 0, cursor_flags: CursorFlags::empty(), }, @@ -265,7 +268,7 @@ impl WindowFlags { let (style, style_ex) = new.to_window_styles(); unsafe { - winuser::SendMessageW(window, *events_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 1, 0); + winuser::SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 1, 0); winuser::SetWindowLongW(window, winuser::GWL_STYLE, style as _); winuser::SetWindowLongW(window, winuser::GWL_EXSTYLE, style_ex as _); @@ -299,7 +302,7 @@ impl WindowFlags { ); } } - winuser::SendMessageW(window, *events_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 0, 0); + winuser::SendMessageW(window, *event_loop::SET_RETAIN_STATE_ON_SIZE_MSG_ID, 0, 0); } } } diff --git a/src/window.rs b/src/window.rs index 1bc0dbf6183..8efec1ada8f 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,20 +1,172 @@ -use std::collections::vec_deque::IntoIter as VecDequeIter; - -use { - CreationError, - EventsLoop, - Icon, - LogicalPosition, - LogicalSize, - MouseCursor, - PhysicalPosition, - PhysicalSize, - platform, - Window, - WindowBuilder, - WindowId, -}; +//! The `Window` struct and associated types. +use std::{fmt, error}; + +use platform_impl; +use event_loop::EventLoopWindowTarget; +use monitor::{AvailableMonitorsIter, MonitorHandle}; +use dpi::{LogicalPosition, LogicalSize}; + +pub use icon::*; + +/// Represents a window. +/// +/// # Example +/// +/// ```no_run +/// use winit::window::Window; +/// use winit::event::{Event, WindowEvent}; +/// use winit::event_loop::{EventLoop, ControlFlow}; +/// +/// let mut event_loop = EventLoop::new(); +/// let window = Window::new(&event_loop).unwrap(); +/// +/// event_loop.run(move |event, _, control_flow| { +/// match event { +/// Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => { +/// *control_flow = ControlFlow::Exit +/// }, +/// _ => *control_flow = ControlFlow::Wait, +/// } +/// }); +/// ``` +pub struct Window { + pub(crate) window: platform_impl::Window, +} + +impl fmt::Debug for Window { + fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { + fmtr.pad("Window { .. }") + } +} + +/// Identifier of a window. Unique for each window. +/// +/// Can be obtained with `window.id()`. +/// +/// Whenever you receive an event specific to a window, this event contains a `WindowId` which you +/// can then compare to the ids of your windows. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WindowId(pub(crate) platform_impl::WindowId); + +impl WindowId { + /// Returns a dummy `WindowId`, useful for unit testing. The only guarantee made about the return + /// value of this function is that it will always be equal to itself and to future values returned + /// by this function. No other guarantees are made. This may be equal to a real `WindowId`. + /// + /// **Passing this into a winit function will result in undefined behavior.** + pub unsafe fn dummy() -> Self { + WindowId(platform_impl::WindowId::dummy()) + } +} + +/// Object that allows you to build windows. +#[derive(Clone)] +pub struct WindowBuilder { + /// The attributes to use to create the window. + pub window: WindowAttributes, + + // Platform-specific configuration. Private. + pub(crate) platform_specific: platform_impl::PlatformSpecificWindowBuilderAttributes, +} + +impl fmt::Debug for WindowBuilder { + fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { + fmtr.debug_struct("WindowBuilder") + .field("window", &self.window) + .finish() + } +} + +/// Attributes to use when creating a window. +#[derive(Debug, Clone)] +pub struct WindowAttributes { + /// The dimensions of the window. If this is `None`, some platform-specific dimensions will be + /// used. + /// + /// The default is `None`. + pub dimensions: Option, + + /// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved). + /// + /// The default is `None`. + pub min_dimensions: Option, + + /// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform. + /// + /// The default is `None`. + pub max_dimensions: Option, + + /// Whether the window is resizable or not. + /// + /// The default is `true`. + pub resizable: bool, + + /// Whether the window should be set as fullscreen upon creation. + /// + /// The default is `None`. + pub fullscreen: Option, + + /// The title of the window in the title bar. + /// + /// The default is `"winit window"`. + pub title: String, + + /// Whether the window should be maximized upon creation. + /// + /// The default is `false`. + pub maximized: bool, + + /// Whether the window should be immediately visible upon creation. + /// + /// The default is `true`. + pub visible: bool, + + /// Whether the the window should be transparent. If this is true, writing colors + /// with alpha values different than `1.0` will produce a transparent window. + /// + /// The default is `false`. + pub transparent: bool, + + /// Whether the window should have borders and bars. + /// + /// The default is `true`. + pub decorations: bool, + + /// Whether the window should always be on top of other windows. + /// + /// The default is `false`. + pub always_on_top: bool, + /// The window icon. + /// + /// The default is `None`. + pub window_icon: Option, + + /// [iOS only] Enable multitouch, + /// see [multipleTouchEnabled](https://developer.apple.com/documentation/uikit/uiview/1622519-multipletouchenabled) + pub multitouch: bool, +} + +impl Default for WindowAttributes { + #[inline] + fn default() -> WindowAttributes { + WindowAttributes { + dimensions: None, + min_dimensions: None, + max_dimensions: None, + resizable: true, + title: "winit window".to_owned(), + maximized: false, + fullscreen: None, + visible: true, + transparent: false, + decorations: true, + always_on_top: false, + window_icon: None, + multitouch: false, + } + } +} impl WindowBuilder { /// Initializes a new `WindowBuilder` with default values. #[inline] @@ -69,10 +221,10 @@ impl WindowBuilder { self } - /// Sets the window fullscreen state. None means a normal window, Some(MonitorId) + /// Sets the window fullscreen state. None means a normal window, Some(MonitorHandle) /// means a fullscreen window on that specific monitor #[inline] - pub fn with_fullscreen(mut self, monitor: Option) -> WindowBuilder { + pub fn with_fullscreen(mut self, monitor: Option) -> WindowBuilder { self.window.fullscreen = monitor; self } @@ -142,7 +294,7 @@ impl WindowBuilder { /// Error should be very rare and only occur in case of permission denied, incompatible system, /// out of memory, etc. #[inline] - pub fn build(mut self, events_loop: &EventsLoop) -> Result { + pub fn build(mut self, window_target: &EventLoopWindowTarget) -> Result { self.window.dimensions = Some(self.window.dimensions.unwrap_or_else(|| { if let Some(ref monitor) = self.window.fullscreen { // resizing the window to the dimensions of the monitor when fullscreen @@ -154,8 +306,8 @@ impl WindowBuilder { })); // building - platform::Window::new( - &events_loop.events_loop, + platform_impl::Window::new( + &window_target.p, self.window, self.platform_specific, ).map(|window| Window { window }) @@ -165,14 +317,14 @@ impl WindowBuilder { impl Window { /// Creates a new Window for platforms where this is appropriate. /// - /// This function is equivalent to `WindowBuilder::new().build(events_loop)`. + /// This function is equivalent to `WindowBuilder::new().build(event_loop)`. /// /// Error should be very rare and only occur in case of permission denied, incompatible system, /// out of memory, etc. #[inline] - pub fn new(events_loop: &EventsLoop) -> Result { + pub fn new(event_loop: &EventLoopWindowTarget) -> Result { let builder = WindowBuilder::new(); - builder.build(events_loop) + builder.build(event_loop) } /// Modifies the title of the window. @@ -205,6 +357,21 @@ impl Window { self.window.hide() } + /// Emits a `WindowEvent::RedrawRequested` event in the associated event loop after all OS + /// events have been processed by the event loop. + /// + /// This is the **strongly encouraged** method of redrawing windows, as it can integrates with + /// OS-requested redraws (e.g. when a window gets resized). + /// + /// This function can cause `RedrawRequested` events to be emitted after `Event::EventsCleared` + /// but before `Event::NewEvents` if called in the following circumstances: + /// * While processing `EventsCleared`. + /// * While processing a `RedrawRequested` event that was sent during `EventsCleared` or any + /// directly subsequent `RedrawRequested` event. + pub fn request_redraw(&self) { + self.window.request_redraw() + } + /// Returns the position of the top-left hand corner of the window relative to the /// top-left hand corner of the desktop. /// @@ -365,7 +532,7 @@ impl Window { /// Sets the window to fullscreen or back #[inline] - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, monitor: Option) { self.window.set_fullscreen(monitor) } @@ -402,13 +569,13 @@ impl Window { /// Returns the monitor on which the window currently resides #[inline] - pub fn get_current_monitor(&self) -> MonitorId { + pub fn get_current_monitor(&self) -> MonitorHandle { self.window.get_current_monitor() } /// Returns the list of all the monitors available on the system. /// - /// This is the same as `EventsLoop::get_available_monitors`, and is provided for convenience. + /// This is the same as `EventLoop::get_available_monitors`, and is provided for convenience. #[inline] pub fn get_available_monitors(&self) -> AvailableMonitorsIter { let data = self.window.get_available_monitors(); @@ -417,78 +584,107 @@ impl Window { /// Returns the primary monitor of the system. /// - /// This is the same as `EventsLoop::get_primary_monitor`, and is provided for convenience. + /// This is the same as `EventLoop::get_primary_monitor`, and is provided for convenience. #[inline] - pub fn get_primary_monitor(&self) -> MonitorId { - MonitorId { inner: self.window.get_primary_monitor() } + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle { inner: self.window.get_primary_monitor() } } + /// Returns an identifier unique to the window. #[inline] pub fn id(&self) -> WindowId { WindowId(self.window.id()) } } -/// An iterator for the list of available monitors. -// Implementation note: we retrieve the list once, then serve each element by one by one. -// This may change in the future. -#[derive(Debug)] -pub struct AvailableMonitorsIter { - pub(crate) data: VecDequeIter, +/// Error that can happen while creating a window or a headless renderer. +#[derive(Debug, Clone)] +pub enum CreationError { + OsError(String), + /// TODO: remove this error + NotSupported, } -impl Iterator for AvailableMonitorsIter { - type Item = MonitorId; - - #[inline] - fn next(&mut self) -> Option { - self.data.next().map(|id| MonitorId { inner: id }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.data.size_hint() +impl CreationError { + fn to_string(&self) -> &str { + match *self { + CreationError::OsError(ref text) => &text, + CreationError::NotSupported => "Some of the requested attributes are not supported", + } } } -/// Identifier for a monitor. -#[derive(Debug, Clone)] -pub struct MonitorId { - pub(crate) inner: platform::MonitorId -} - -impl MonitorId { - /// Returns a human-readable name of the monitor. - /// - /// Returns `None` if the monitor doesn't exist anymore. - #[inline] - pub fn get_name(&self) -> Option { - self.inner.get_name() +impl fmt::Display for CreationError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + formatter.write_str(self.to_string()) } +} - /// Returns the monitor's resolution. - #[inline] - pub fn get_dimensions(&self) -> PhysicalSize { - self.inner.get_dimensions() +impl error::Error for CreationError { + fn description(&self) -> &str { + self.to_string() } +} - /// Returns the top-left corner position of the monitor relative to the larger full - /// screen area. - #[inline] - pub fn get_position(&self) -> PhysicalPosition { - self.inner.get_position() - } +/// Describes the appearance of the mouse cursor. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum MouseCursor { + /// The platform-dependent default cursor. + Default, + /// A simple crosshair. + Crosshair, + /// A hand (often used to indicate links in web browsers). + Hand, + /// Self explanatory. + Arrow, + /// Indicates something is to be moved. + Move, + /// Indicates text that may be selected or edited. + Text, + /// Program busy indicator. + Wait, + /// Help indicator (often rendered as a "?") + Help, + /// Progress indicator. Shows that processing is being done. But in contrast + /// with "Wait" the user may still interact with the program. Often rendered + /// as a spinning beach ball, or an arrow with a watch or hourglass. + Progress, + + /// Cursor showing that something cannot be done. + NotAllowed, + ContextMenu, + Cell, + VerticalText, + Alias, + Copy, + NoDrop, + Grab, + Grabbing, + AllScroll, + ZoomIn, + ZoomOut, + + /// Indicate that some edge is to be moved. For example, the 'SeResize' cursor + /// is used when the movement starts from the south-east corner of the box. + EResize, + NResize, + NeResize, + NwResize, + SResize, + SeResize, + SwResize, + WResize, + EwResize, + NsResize, + NeswResize, + NwseResize, + ColResize, + RowResize, +} - /// Returns the DPI factor that can be used to map logical pixels to physical pixels, and vice versa. - /// - /// See the [`dpi`](dpi/index.html) module for more information. - /// - /// ## Platform-specific - /// - /// - **X11:** This respects Xft.dpi XResource, and can be overridden using the `WINIT_HIDPI_FACTOR` environment variable. - /// - **Android:** Always returns 1.0. - #[inline] - pub fn get_hidpi_factor(&self) -> f64 { - self.inner.get_hidpi_factor() +impl Default for MouseCursor { + fn default() -> Self { + MouseCursor::Default } } diff --git a/tests/send_objects.rs b/tests/send_objects.rs index f3328c100d3..9220203fd41 100644 --- a/tests/send_objects.rs +++ b/tests/send_objects.rs @@ -3,21 +3,23 @@ extern crate winit; fn needs_send() {} #[test] -fn events_loop_proxy_send() { - // ensures that `winit::EventsLoopProxy` implements `Send` - needs_send::(); +fn event_loop_proxy_send() { + fn is_send() { + // ensures that `winit::EventLoopProxy` implements `Send` + needs_send::>(); + } } #[test] fn window_send() { // ensures that `winit::Window` implements `Send` - needs_send::(); + needs_send::(); } #[test] fn ids_send() { // ensures that the various `..Id` types implement `Send` - needs_send::(); - needs_send::(); - needs_send::(); + needs_send::(); + needs_send::(); + needs_send::(); } diff --git a/tests/serde_objects.rs b/tests/serde_objects.rs index b5a49631b6a..5effbbda09e 100644 --- a/tests/serde_objects.rs +++ b/tests/serde_objects.rs @@ -3,8 +3,8 @@ extern crate serde; extern crate winit; -use winit::{ControlFlow, MouseCursor}; -use winit::{ +use winit::window::{MouseCursor}; +use winit::event::{ KeyboardInput, TouchPhase, ElementState, MouseButton, MouseScrollDelta, VirtualKeyCode, ModifiersState }; @@ -14,8 +14,7 @@ use serde::{Serialize, Deserialize}; fn needs_serde>() {} #[test] -fn root_serde() { - needs_serde::(); +fn window_serde() { needs_serde::(); } diff --git a/tests/sync_object.rs b/tests/sync_object.rs index eb9a06e3447..c59cc077fc3 100644 --- a/tests/sync_object.rs +++ b/tests/sync_object.rs @@ -5,5 +5,5 @@ fn needs_sync() {} #[test] fn window_sync() { // ensures that `winit::Window` implements `Sync` - needs_sync::(); + needs_sync::(); }