From f7d7acb3c54965fe99c6c9a12ef5b13f55a65c45 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Sun, 7 Apr 2019 15:58:47 +0200 Subject: [PATCH] Cleanup some previous merge errors --- src/platform/windows/events_loop.rs | 1258 ----------------- src/platform_impl/macos/util.rs | 38 - .../macos/util/cursor.rs | 0 .../macos/util/into_option.rs | 0 .../macos/util/mod.rs | 4 +- 5 files changed, 2 insertions(+), 1298 deletions(-) delete mode 100644 src/platform/windows/events_loop.rs delete mode 100644 src/platform_impl/macos/util.rs rename src/{platform => platform_impl}/macos/util/cursor.rs (100%) rename src/{platform => platform_impl}/macos/util/into_option.rs (100%) rename src/{platform => platform_impl}/macos/util/mod.rs (96%) diff --git a/src/platform/windows/events_loop.rs b/src/platform/windows/events_loop.rs deleted file mode 100644 index a8882339..00000000 --- a/src/platform/windows/events_loop.rs +++ /dev/null @@ -1,1258 +0,0 @@ -//! An events loop on Win32 is a background thread. -//! -//! Creating an events loop spawns a thread and blocks it in a permanent Win32 events loop. -//! Destroying the events loop stops the thread. -//! -//! You can use the `execute_in_thread` method to execute some code in the background thread. -//! Since Win32 requires you to create a window in the right thread, you must use this method -//! to create a window. -//! -//! If you create a window whose class is set to `callback`, the window's events will be -//! propagated with `run_forever` and `poll_events`. -//! The closure passed to the `execute_in_thread` method takes an `Inserter` that you can use to -//! add a `WindowState` entry to a list of window to be used by the callback. - -use std::{mem, panic, ptr, thread}; -use std::any::Any; -use std::cell::RefCell; -use std::collections::HashMap; -use std::os::windows::io::AsRawHandle; -use std::sync::{Arc, mpsc, Mutex}; - -use backtrace::Backtrace; -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; -use winapi::shared::winerror::S_OK; -use winapi::um::{libloaderapi, processthreadsapi, ole2, winuser}; -use winapi::um::oleidl::LPDROPTARGET; -use winapi::um::winnt::{LONG, LPCSTR, SHORT}; - -use { - ControlFlow, - Event, - EventsLoopClosed, - KeyboardInput, - LogicalPosition, - LogicalSize, - PhysicalSize, - WindowEvent, - WindowId as SuperWindowId, -}; -use events::{DeviceEvent, Touch, TouchPhase}; -use platform::platform::{event, WindowId, DEVICE_ID, wrap_device_id, util}; -use platform::platform::dpi::{ - become_dpi_aware, - dpi_to_scale_factor, - enable_non_client_dpi_scaling, - get_hwnd_scale_factor, -}; -use platform::platform::drop_handler::FileDropHandler; -use platform::platform::event::{handle_extended_keys, process_key_params, vkey_to_winit_vkey}; -use platform::platform::raw_input::{get_raw_input_data, get_raw_mouse_button_state}; -use platform::platform::window::adjust_size; -use platform::platform::window_state::{CursorFlags, WindowFlags, WindowState}; - -/// Dummy object that allows inserting a window's state. -// We store a pointer in order to !impl Send and Sync. -pub struct Inserter(*mut u8); - -impl Inserter { - /// Inserts a window's state for the callback to use. The state is removed automatically if the - /// callback receives a `WM_CLOSE` message for the window. - pub fn insert(&self, window: HWND, state: Arc>) { - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - let was_in = context_stash.as_mut().unwrap().windows.insert(window, state); - assert!(was_in.is_none()); - }); - } -} - -pub struct EventsLoop { - thread_msg_target: HWND, - // Id of the background thread from the Win32 API. - thread_id: DWORD, - // Receiver for the events. The sender is in the background thread. - receiver: mpsc::Receiver, - // Sender instance that's paired with the receiver. Used to construct an `EventsLoopProxy`. - sender: mpsc::Sender, -} - -enum EventsLoopEvent { - WinitEvent(Event), - Panic(PanicError), -} - -impl EventsLoop { - pub fn new() -> EventsLoop { - Self::with_dpi_awareness(true) - } - - pub fn with_dpi_awareness(dpi_aware: bool) -> EventsLoop { - struct InitData { - thread_msg_target: HWND, - } - unsafe impl Send for InitData {} - - become_dpi_aware(dpi_aware); - - // The main events transfer channel. - let (tx, rx) = mpsc::channel(); - - // Channel to send initialization data created on the event loop thread back to the main - // thread. - let (init_tx, init_rx) = mpsc::sync_channel(0); - - let thread_sender = tx.clone(); - let panic_sender = tx.clone(); - let thread = thread::spawn(move || { - let tx = thread_sender; - let thread_msg_target = thread_event_target_window(); - - CONTEXT_STASH.with(|context_stash| { - *context_stash.borrow_mut() = Some(ThreadLocalData { - sender: tx, - windows: HashMap::with_capacity(4), - file_drop_handlers: HashMap::with_capacity(4), - mouse_buttons_down: 0, - panic_error: None, - }); - }); - - unsafe { - // Calling `PostThreadMessageA` on a thread that does not have an events queue yet - // will fail. In order to avoid this situation, we call `IsGuiThread` to initialize - // it. - winuser::IsGUIThread(1); - // Then only we unblock the `new()` function. We are sure that we don't call - // `PostThreadMessageA()` before `new()` returns. - init_tx.send(InitData{ thread_msg_target }).ok(); - drop(init_tx); - - let mut msg = mem::uninitialized(); - - loop { - if winuser::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 { - // If a panic occurred in the child callback, forward the panic information - // to the parent thread. - let panic_payload_opt = CONTEXT_STASH.with(|stash| - stash.borrow_mut().as_mut() - .and_then(|s| s.panic_error.take()) - ); - if let Some(panic_payload) = panic_payload_opt { - panic_sender.send(EventsLoopEvent::Panic(panic_payload)).unwrap(); - }; - - // Only happens if the message is `WM_QUIT`. - debug_assert_eq!(msg.message, winuser::WM_QUIT); - break; - } - - // Calls `callback` below. - winuser::TranslateMessage(&msg); - winuser::DispatchMessageW(&msg); - } - } - }); - - // Blocks this function until the background thread has an events loop. See other comments. - let InitData { thread_msg_target } = init_rx.recv().unwrap(); - - let thread_id = unsafe { - let handle = mem::transmute(thread.as_raw_handle()); - processthreadsapi::GetThreadId(handle) - }; - - EventsLoop { - thread_msg_target, - thread_id, - receiver: rx, - sender: tx, - } - } - - pub fn poll_events(&mut self, mut callback: F) - where F: FnMut(Event) - { - loop { - let event = match self.receiver.try_recv() { - Ok(EventsLoopEvent::WinitEvent(e)) => e, - Ok(EventsLoopEvent::Panic(panic)) => { - eprintln!("resuming child thread unwind at: {:?}", Backtrace::new()); - panic::resume_unwind(panic) - }, - Err(_) => break, - }; - - callback(event); - } - } - - pub fn run_forever(&mut self, mut callback: F) - where F: FnMut(Event) -> ControlFlow - { - loop { - let event = match self.receiver.recv() { - Ok(EventsLoopEvent::WinitEvent(e)) => e, - Ok(EventsLoopEvent::Panic(panic)) => { - eprintln!("resuming child thread unwind at: {:?}", Backtrace::new()); - panic::resume_unwind(panic) - }, - Err(_) => break, - }; - - let flow = callback(event); - match flow { - ControlFlow::Continue => continue, - ControlFlow::Break => break, - } - } - } - - pub fn create_proxy(&self) -> EventsLoopProxy { - EventsLoopProxy { - thread_id: self.thread_id, - thread_msg_target: self.thread_msg_target, - sender: self.sender.clone(), - } - } - - /// Executes a function in the background thread. - /// - /// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent - /// to the unstable FnBox. - /// - /// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is - /// removed automatically if the callback receives a `WM_CLOSE` message for the window. - pub(super) fn execute_in_thread(&self, function: F) - where F: FnMut(Inserter) + Send + 'static - { - self.create_proxy().execute_in_thread(function) - } -} - -impl Drop for EventsLoop { - fn drop(&mut self) { - unsafe { - // Posting `WM_QUIT` will cause `GetMessage` to stop. - winuser::PostThreadMessageA(self.thread_id, winuser::WM_QUIT, 0, 0); - } - } -} - -#[derive(Clone)] -pub struct EventsLoopProxy { - thread_id: DWORD, - thread_msg_target: HWND, - sender: mpsc::Sender, -} - -unsafe impl Send for EventsLoopProxy {} -unsafe impl Sync for EventsLoopProxy {} - -impl EventsLoopProxy { - pub fn wakeup(&self) -> Result<(), EventsLoopClosed> { - self.sender.send(EventsLoopEvent::WinitEvent(Event::Awakened)).map_err(|_| EventsLoopClosed) - } - - /// Executes a function in the background thread. - /// - /// Note that we use FnMut instead of FnOnce because boxing FnOnce won't work on stable Rust - /// until 2030 when the design of Box is finally complete. - /// https://github.com/rust-lang/rust/issues/28796 - /// - /// 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. - pub fn execute_in_thread(&self, mut function: F) - where - F: FnMut(Inserter) + Send + 'static, - { - if unsafe{ processthreadsapi::GetCurrentThreadId() } == self.thread_id { - function(Inserter(ptr::null_mut())); - } else { - // We are using double-boxing here because it make casting back much easier - let double_box: ThreadExecFn = Box::new(Box::new(function) as Box); - let raw = Box::into_raw(double_box); - - let res = unsafe { - winuser::PostMessageW( - self.thread_msg_target, - *EXEC_MSG_ID, - raw as *mut () as usize as WPARAM, - 0, - ) - }; - assert!(res != 0, "PostMessage failed; is the messages queue full?"); - } - } -} - -type ThreadExecFn = Box>; - -lazy_static! { - // 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 LPCSTR) - } - }; - // 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) - } - }; - // 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(thread_event_target_callback), - 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() -> HWND { - 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, - (winuser::WS_VISIBLE | winuser::WS_POPUP) as _ - ); - - window - } -} - -// There's no parameters passed to the callback function, so it needs to get its context stashed -// in a thread-local variable. -thread_local!(static CONTEXT_STASH: RefCell> = RefCell::new(None)); -struct ThreadLocalData { - sender: mpsc::Sender, - windows: HashMap>>, - file_drop_handlers: HashMap, // Each window has its own drop handler. - mouse_buttons_down: u32, - panic_error: Option, -} -type PanicError = Box; - -// Utility function that dispatches an event on the current thread. -pub fn send_event(event: Event) { - CONTEXT_STASH.with(|context_stash| { - let context_stash = context_stash.borrow(); - - let _ = context_stash.as_ref().unwrap().sender.send(EventsLoopEvent::WinitEvent(event)); // Ignoring if closed - }); -} - -/// Capture mouse input, allowing `window` to receive mouse events when the cursor is outside of -/// the window. -unsafe fn capture_mouse(window: HWND) { - let set_capture = CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - context_stash.mouse_buttons_down += 1; - true - } else { - false - } - }); - if set_capture { - 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() { - let release_capture = CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - context_stash.mouse_buttons_down = context_stash.mouse_buttons_down.saturating_sub(1); - if context_stash.mouse_buttons_down == 0 { - return true; - } - } - false - }); - if release_capture { - winuser::ReleaseCapture(); - } -} - -pub unsafe fn run_catch_panic(error: R, f: F) -> R - where F: panic::UnwindSafe + FnOnce() -> R -{ - // If a panic has been triggered, cancel all future operations in the function. - if CONTEXT_STASH.with(|stash| stash.borrow().as_ref().map(|s| s.panic_error.is_some()).unwrap_or(false)) { - return error; - } - - let callback_result = panic::catch_unwind(f); - match callback_result { - Ok(lresult) => lresult, - Err(err) => CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - context_stash.panic_error = Some(err); - winuser::PostQuitMessage(-1); - } - error - }) - } -} - -/// Any window whose callback is configured to this function will have its events propagated -/// through the events loop of the thread the window was created in. -// -// This is the callback that is called by `DispatchMessage` in the events loop. -// -// Returning 0 tells the Win32 API that the message has been processed. -// FIXME: detect WM_DWMCOMPOSITIONCHANGED and call DwmEnableBlurBehindWindow if necessary -pub unsafe extern "system" fn callback( - window: HWND, - msg: UINT, - wparam: WPARAM, - lparam: LPARAM, -) -> LRESULT { - // Unwinding into foreign code is undefined behavior. So we catch any panics that occur in our - // code, and if a panic happens we cancel any future operations. - run_catch_panic(-1, || callback_inner(window, msg, wparam, lparam)) -} - -unsafe fn callback_inner( - window: HWND, - msg: UINT, - wparam: WPARAM, - lparam: LPARAM, -) -> LRESULT { - match msg { - winuser::WM_CREATE => { - use winapi::shared::winerror::{OLE_E_WRONGCOMPOBJ, RPC_E_CHANGED_MODE}; - 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`"); - } - - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - - let drop_handlers = &mut context_stash.as_mut().unwrap().file_drop_handlers; - let new_handler = FileDropHandler::new(window); - let handler_interface_ptr = &mut (*new_handler.data).interface as LPDROPTARGET; - drop_handlers.insert(window, new_handler); - - assert_eq!(ole2::RegisterDragDrop(window, handler_interface_ptr), S_OK); - }); - 0 - }, - - winuser::WM_NCCREATE => { - enable_non_client_dpi_scaling(window); - winuser::DefWindowProcW(window, msg, wparam, lparam) - }, - - winuser::WM_CLOSE => { - use events::WindowEvent::CloseRequested; - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: CloseRequested - }); - 0 - }, - - winuser::WM_DESTROY => { - use events::WindowEvent::Destroyed; - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - ole2::RevokeDragDrop(window); - let context_stash_mut = context_stash.as_mut().unwrap(); - context_stash_mut.file_drop_handlers.remove(&window); - context_stash_mut.windows.remove(&window); - }); - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: Destroyed - }); - 0 - }, - - winuser::WM_PAINT => { - use events::WindowEvent::Refresh; - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: Refresh, - }); - winuser::DefWindowProcW(window, msg, wparam, lparam) - }, - - // WM_MOVE supplies client area positions, so we send Moved here instead. - winuser::WM_WINDOWPOSCHANGED => { - use events::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, - ); - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: Moved(logical_position), - }); - } - - // This is necessary for us to still get sent WM_SIZE. - winuser::DefWindowProcW(window, msg, wparam, lparam) - }, - - winuser::WM_SIZE => { - use events::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: SuperWindowId(WindowId(window)), - event: Resized(logical_size), - }; - - // Wait for the parent thread to process the resize event before returning from the - // callback. - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - let cstash = context_stash.as_mut().unwrap(); - - if let Some(w) = cstash.windows.get_mut(&window) { - let mut w = w.lock().unwrap(); - - // 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)); - } - } - - cstash.sender.send(EventsLoopEvent::WinitEvent(event)).ok(); - }); - 0 - }, - - winuser::WM_CHAR => { - use std::mem; - use events::WindowEvent::ReceivedCharacter; - let chr: char = mem::transmute(wparam as u32); - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: ReceivedCharacter(chr), - }); - 0 - }, - - // Prevents default windows menu hotkeys playing unwanted - // "ding" sounds. Alternatively could check for WM_SYSCOMMAND - // with wparam being SC_KEYMENU, but this may prevent some - // other unwanted default hotkeys as well. - winuser::WM_SYSCHAR => { - 0 - } - - winuser::WM_MOUSEMOVE => { - use events::WindowEvent::{CursorEntered, CursorMoved}; - let x = windowsx::GET_X_LPARAM(lparam); - let y = windowsx::GET_Y_LPARAM(lparam); - - let mouse_was_outside_window = CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - if let Some(w) = context_stash.windows.get_mut(&window) { - let mut w = w.lock().unwrap(); - - 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(); - return was_outside_window; - } - } - - false - }); - - - if mouse_was_outside_window { - send_event(Event::WindowEvent { - window_id: SuperWindowId(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 dpi_factor = get_hwnd_scale_factor(window); - let position = LogicalPosition::from_physical((x as f64, y as f64), dpi_factor); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() }, - }); - - 0 - }, - - winuser::WM_MOUSELEAVE => { - use events::WindowEvent::CursorLeft; - - CONTEXT_STASH.with(|context_stash| { - let mut context_stash = context_stash.borrow_mut(); - if let Some(context_stash) = context_stash.as_mut() { - if let Some(w) = context_stash.windows.get_mut(&window) { - let mut w = w.lock().unwrap(); - w.mouse.set_cursor_flags(window, |f| f.set(CursorFlags::IN_WINDOW, false)).ok(); - } - } - }); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: CursorLeft { device_id: DEVICE_ID } - }); - - 0 - }, - - winuser::WM_MOUSEWHEEL => { - use events::MouseScrollDelta::LineDelta; - use events::TouchPhase; - - let value = (wparam >> 16) as i16; - let value = value as i32; - let value = value as f32 / winuser::WHEEL_DELTA as f32; - - send_event(Event::WindowEvent { - window_id: SuperWindowId(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 events::ElementState::Pressed; - use events::VirtualKeyCode; - if msg == winuser::WM_SYSKEYDOWN && wparam as i32 == winuser::VK_F4 { - winuser::DefWindowProcW(window, msg, wparam, lparam) - } else { - if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: Pressed, - scancode: scancode, - virtual_keycode: vkey, - modifiers: event::get_key_mods(), - } - } - }); - // 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) { - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: WindowEvent::ReceivedCharacter('\u{7F}'), - }); - } - } - 0 - } - }, - - winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { - use events::ElementState::Released; - if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: WindowEvent::KeyboardInput { - device_id: DEVICE_ID, - input: KeyboardInput { - state: Released, - scancode: scancode, - virtual_keycode: vkey, - modifiers: event::get_key_mods(), - }, - } - }); - } - 0 - }, - - winuser::WM_LBUTTONDOWN => { - use events::WindowEvent::MouseInput; - use events::MouseButton::Left; - use events::ElementState::Pressed; - - capture_mouse(window); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left, modifiers: event::get_key_mods() } - }); - 0 - }, - - winuser::WM_LBUTTONUP => { - use events::WindowEvent::MouseInput; - use events::MouseButton::Left; - use events::ElementState::Released; - - release_mouse(); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Left, modifiers: event::get_key_mods() } - }); - 0 - }, - - winuser::WM_RBUTTONDOWN => { - use events::WindowEvent::MouseInput; - use events::MouseButton::Right; - use events::ElementState::Pressed; - - capture_mouse(window); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right, modifiers: event::get_key_mods() } - }); - 0 - }, - - winuser::WM_RBUTTONUP => { - use events::WindowEvent::MouseInput; - use events::MouseButton::Right; - use events::ElementState::Released; - - release_mouse(); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Right, modifiers: event::get_key_mods() } - }); - 0 - }, - - winuser::WM_MBUTTONDOWN => { - use events::WindowEvent::MouseInput; - use events::MouseButton::Middle; - use events::ElementState::Pressed; - - capture_mouse(window); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle, modifiers: event::get_key_mods() } - }); - 0 - }, - - winuser::WM_MBUTTONUP => { - use events::WindowEvent::MouseInput; - use events::MouseButton::Middle; - use events::ElementState::Released; - - release_mouse(); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: MouseInput { device_id: DEVICE_ID, state: Released, button: Middle, modifiers: event::get_key_mods() } - }); - 0 - }, - - winuser::WM_XBUTTONDOWN => { - use events::WindowEvent::MouseInput; - use events::MouseButton::Other; - use events::ElementState::Pressed; - let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - - capture_mouse(window); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(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 events::WindowEvent::MouseInput; - use events::MouseButton::Other; - use events::ElementState::Released; - let xbutton = winuser::GET_XBUTTON_WPARAM(wparam); - - release_mouse(); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(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!(), - }; - - send_event(Event::DeviceEvent { - device_id: wrap_device_id(lparam as _), - event, - }); - - winuser::DefWindowProcW(window, msg, wparam, lparam) - }, - - winuser::WM_INPUT => { - use events::DeviceEvent::{Motion, MouseMotion, MouseWheel, Button, Key}; - use events::MouseScrollDelta::LineDelta; - use events::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 { - send_event(Event::DeviceEvent { - device_id, - event: Motion { axis: 0, value: x } - }); - } - - if y != 0.0 { - send_event(Event::DeviceEvent { - device_id, - event: Motion { axis: 1, value: y } - }); - } - - if x != 0.0 || y != 0.0 { - 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; - 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 _; - 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); - - send_event(Event::DeviceEvent { - device_id, - event: Key(KeyboardInput { - scancode, - state, - virtual_keycode, - modifiers: event::get_key_mods(), - }), - }); - } - } - } - } - - winuser::DefWindowProcW(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); - send_event( Event::WindowEvent { - window_id: SuperWindowId(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 events::WindowEvent::{Focused, CursorMoved}; - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: Focused(true) - }); - - let x = windowsx::GET_X_LPARAM(lparam) as f64; - let y = windowsx::GET_Y_LPARAM(lparam) as f64; - let dpi_factor = get_hwnd_scale_factor(window); - let position = LogicalPosition::from_physical((x, y), dpi_factor); - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: CursorMoved { device_id: DEVICE_ID, position, modifiers: event::get_key_mods() }, - }); - - 0 - }, - - winuser::WM_KILLFOCUS => { - use events::WindowEvent::Focused; - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: Focused(false) - }); - 0 - }, - - winuser::WM_SETCURSOR => { - let set_cursor_to = CONTEXT_STASH.with(|context_stash| { - context_stash - .borrow() - .as_ref() - .and_then(|cstash| cstash.windows.get(&window)) - .and_then(|window_state_mutex| { - let window_state = window_state_mutex.lock().unwrap(); - 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; - //(*mmi).max_position = winapi::shared::windef::POINT { x: -8, y: -8 }; // The upper left corner of the window if it were maximized on the primary monitor. - //(*mmi).max_size = winapi::shared::windef::POINT { x: .., y: .. }; // The dimensions of the primary monitor. - - CONTEXT_STASH.with(|context_stash| { - if let Some(cstash) = context_stash.borrow().as_ref() { - if let Some(wstash) = cstash.windows.get(&window) { - let window_state = wstash.lock().unwrap(); - - 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 events::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 = CONTEXT_STASH.with(|context_stash| { - if let Some(wstash) = context_stash.borrow().as_ref().and_then(|cstash| cstash.windows.get(&window)) { - let mut window_state = wstash.lock().unwrap(); - 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() - } else { - true - } - }); - - // 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, - ); - } - - send_event(Event::WindowEvent { - window_id: SuperWindowId(WindowId(window)), - event: HiDpiFactorChanged(new_dpi_factor), - }); - - 0 - }, - - _ => { - if msg == *DESTROY_MSG_ID { - winuser::DestroyWindow(window); - 0 - } else if msg == *INITIAL_DPI_MSG_ID { - use events::WindowEvent::HiDpiFactorChanged; - let scale_factor = dpi_to_scale_factor(wparam as u32); - send_event(Event::WindowEvent { - window_id: SuperWindowId(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 if msg == *SET_RETAIN_STATE_ON_SIZE_MSG_ID { - CONTEXT_STASH.with(|context_stash| { - if let Some(cstash) = context_stash.borrow().as_ref() { - if let Some(wstash) = cstash.windows.get(&window) { - let mut window_state = wstash.lock().unwrap(); - window_state.set_window_flags_in_place(|f| f.set(WindowFlags::MARKER_RETAIN_STATE_ON_SIZE, wparam != 0)); - } - } - }); - 0 - } else { - winuser::DefWindowProcW(window, msg, wparam, lparam) - } - } - } -} - -pub unsafe extern "system" fn thread_event_target_callback( - window: HWND, - msg: UINT, - wparam: WPARAM, - lparam: LPARAM, -) -> LRESULT { - // See `callback` comment. - run_catch_panic(-1, || { - match msg { - _ if msg == *EXEC_MSG_ID => { - let mut function: ThreadExecFn = Box::from_raw(wparam as usize as *mut _); - function(Inserter(ptr::null_mut())); - 0 - }, - _ => winuser::DefWindowProcW(window, msg, wparam, lparam) - } - }) -} diff --git a/src/platform_impl/macos/util.rs b/src/platform_impl/macos/util.rs deleted file mode 100644 index c4c348f0..00000000 --- a/src/platform_impl/macos/util.rs +++ /dev/null @@ -1,38 +0,0 @@ -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/util/cursor.rs b/src/platform_impl/macos/util/cursor.rs similarity index 100% rename from src/platform/macos/util/cursor.rs rename to src/platform_impl/macos/util/cursor.rs diff --git a/src/platform/macos/util/into_option.rs b/src/platform_impl/macos/util/into_option.rs similarity index 100% rename from src/platform/macos/util/into_option.rs rename to src/platform_impl/macos/util/into_option.rs diff --git a/src/platform/macos/util/mod.rs b/src/platform_impl/macos/util/mod.rs similarity index 96% rename from src/platform/macos/util/mod.rs rename to src/platform_impl/macos/util/mod.rs index baa0e6e0..627f6674 100644 --- a/src/platform/macos/util/mod.rs +++ b/src/platform_impl/macos/util/mod.rs @@ -9,8 +9,8 @@ use cocoa::foundation::{NSRect, NSUInteger}; use core_graphics::display::CGDisplay; use objc::runtime::{Class, Object}; -use platform::platform::ffi; -use platform::platform::window::IdRef; +use platform_impl::platform::ffi; +use platform_impl::platform::window::IdRef; pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange { location: ffi::NSNotFound as NSUInteger,