From 1596cc5d9ee0f34637a5fe17c0de3dd5bc4ebec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 25 Jun 2019 21:36:24 +0200 Subject: [PATCH] Avoid leaking implementation details in `Canvas` API --- .../web/event_loop/window_target.rs | 18 +- src/platform_impl/web/web_sys/canvas.rs | 82 ++-- src/platform_impl/web/web_sys/event.rs | 3 + src/platform_impl/web_sys/event_loop.rs | 378 ------------------ src/platform_impl/web_sys/events.rs | 201 ---------- src/platform_impl/web_sys/mod.rs | 30 -- src/platform_impl/web_sys/window.rs | 330 --------------- 7 files changed, 56 insertions(+), 986 deletions(-) delete mode 100644 src/platform_impl/web_sys/event_loop.rs delete mode 100644 src/platform_impl/web_sys/events.rs delete mode 100644 src/platform_impl/web_sys/mod.rs delete mode 100644 src/platform_impl/web_sys/window.rs diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 5d2f06b2..c8e608e4 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -49,7 +49,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_key_down(move |scancode, virtual_keycode, modifiers| { + canvas.on_keyboard_press(move |scancode, virtual_keycode, modifiers| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::KeyboardInput { @@ -65,7 +65,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_key_up(move |scancode, virtual_keycode, modifiers| { + canvas.on_keyboard_release(move |scancode, virtual_keycode, modifiers| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::KeyboardInput { @@ -81,7 +81,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_key_press(move |char_code| { + canvas.on_received_character(move |char_code| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::ReceivedCharacter(char_code), @@ -89,7 +89,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_mouse_out(move |pointer_id| { + canvas.on_cursor_leave(move |pointer_id| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::CursorLeft { @@ -99,7 +99,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_mouse_over(move |pointer_id| { + canvas.on_cursor_enter(move |pointer_id| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::CursorEntered { @@ -109,7 +109,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_mouse_move(move |pointer_id, position, modifiers| { + canvas.on_cursor_move(move |pointer_id, position, modifiers| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::CursorMoved { @@ -121,7 +121,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_mouse_up(move |pointer_id, button, modifiers| { + canvas.on_mouse_press(move |pointer_id, button, modifiers| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::MouseInput { @@ -134,7 +134,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_mouse_down(move |pointer_id, button, modifiers| { + canvas.on_mouse_release(move |pointer_id, button, modifiers| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::MouseInput { @@ -147,7 +147,7 @@ impl WindowTarget { }); let runner = self.runner.clone(); - canvas.on_mouse_scroll(move |pointer_id, delta, modifiers| { + canvas.on_mouse_wheel(move |pointer_id, delta, modifiers| { runner.send_event(Event::WindowEvent { window_id: WindowId(window::Id), event: WindowEvent::MouseWheel { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index ab7cd571..6d1dd373 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -12,15 +12,15 @@ pub struct Canvas { on_redraw: Closure, on_focus: Option>, on_blur: Option>, - on_key_up: Option>, - on_key_down: Option>, - on_key_press: Option>, - on_mouse_out: Option>, - on_mouse_over: Option>, - on_mouse_up: Option>, - on_mouse_down: Option>, - on_mouse_move: Option>, - on_mouse_scroll: Option>, + on_keyboard_release: Option>, + on_keyboard_press: Option>, + on_received_character: Option>, + on_cursor_leave: Option>, + on_cursor_enter: Option>, + on_cursor_move: Option>, + on_mouse_press: Option>, + on_mouse_release: Option>, + on_mouse_wheel: Option>, } impl Drop for Canvas { @@ -58,15 +58,15 @@ impl Canvas { on_redraw: Closure::wrap(Box::new(on_redraw) as Box), on_blur: None, on_focus: None, - on_key_up: None, - on_key_down: None, - on_key_press: None, - on_mouse_out: None, - on_mouse_over: None, - on_mouse_up: None, - on_mouse_down: None, - on_mouse_move: None, - on_mouse_scroll: None, + on_keyboard_release: None, + on_keyboard_press: None, + on_received_character: None, + on_cursor_leave: None, + on_cursor_enter: None, + on_cursor_move: None, + on_mouse_release: None, + on_mouse_press: None, + on_mouse_wheel: None, }) } @@ -124,11 +124,11 @@ impl Canvas { })); } - pub fn on_key_up(&mut self, mut handler: F) + pub fn on_keyboard_release(&mut self, mut handler: F) where F: 'static + FnMut(ScanCode, Option, ModifiersState), { - self.on_key_up = Some(self.add_event("keyup", move |event: KeyboardEvent| { + self.on_keyboard_release = Some(self.add_event("keyup", move |event: KeyboardEvent| { handler( event::scan_code(&event), event::virtual_key_code(&event), @@ -137,11 +137,11 @@ impl Canvas { })); } - pub fn on_key_down(&mut self, mut handler: F) + pub fn on_keyboard_press(&mut self, mut handler: F) where F: 'static + FnMut(ScanCode, Option, ModifiersState), { - self.on_key_down = Some(self.add_event("keydown", move |event: KeyboardEvent| { + self.on_keyboard_press = Some(self.add_event("keydown", move |event: KeyboardEvent| { handler( event::scan_code(&event), event::virtual_key_code(&event), @@ -150,38 +150,44 @@ impl Canvas { })); } - pub fn on_key_press(&mut self, mut handler: F) + pub fn on_received_character(&mut self, mut handler: F) where F: 'static + FnMut(char), { - self.on_key_press = Some(self.add_event("keypress", move |event: KeyboardEvent| { - handler(event::codepoint(&event)); - })); + // TODO: Use `beforeinput`. + // + // The `keypress` event is deprecated, but there does not seem to be a + // viable/compatible alternative as of now. `beforeinput` is still widely + // unsupported. + self.on_received_character = + Some(self.add_event("keypress", move |event: KeyboardEvent| { + handler(event::codepoint(&event)); + })); } - pub fn on_mouse_out(&mut self, mut handler: F) + pub fn on_cursor_leave(&mut self, mut handler: F) where F: 'static + FnMut(i32), { - self.on_mouse_out = Some(self.add_event("pointerout", move |event: PointerEvent| { + self.on_cursor_leave = Some(self.add_event("pointerout", move |event: PointerEvent| { handler(event.pointer_id()); })); } - pub fn on_mouse_over(&mut self, mut handler: F) + pub fn on_cursor_enter(&mut self, mut handler: F) where F: 'static + FnMut(i32), { - self.on_mouse_over = Some(self.add_event("pointerover", move |event: PointerEvent| { + self.on_cursor_enter = Some(self.add_event("pointerover", move |event: PointerEvent| { handler(event.pointer_id()); })); } - pub fn on_mouse_up(&mut self, mut handler: F) + pub fn on_mouse_release(&mut self, mut handler: F) where F: 'static + FnMut(i32, MouseButton, ModifiersState), { - self.on_mouse_up = Some(self.add_event("pointerup", move |event: PointerEvent| { + self.on_mouse_release = Some(self.add_event("pointerup", move |event: PointerEvent| { handler( event.pointer_id(), event::mouse_button(&event), @@ -190,11 +196,11 @@ impl Canvas { })); } - pub fn on_mouse_down(&mut self, mut handler: F) + pub fn on_mouse_press(&mut self, mut handler: F) where F: 'static + FnMut(i32, MouseButton, ModifiersState), { - self.on_mouse_down = Some(self.add_event("pointerdown", move |event: PointerEvent| { + self.on_mouse_press = Some(self.add_event("pointerdown", move |event: PointerEvent| { handler( event.pointer_id(), event::mouse_button(&event), @@ -203,11 +209,11 @@ impl Canvas { })); } - pub fn on_mouse_move(&mut self, mut handler: F) + pub fn on_cursor_move(&mut self, mut handler: F) where F: 'static + FnMut(i32, LogicalPosition, ModifiersState), { - self.on_mouse_move = Some(self.add_event("pointermove", move |event: PointerEvent| { + self.on_cursor_move = Some(self.add_event("pointermove", move |event: PointerEvent| { handler( event.pointer_id(), event::mouse_position(&event), @@ -216,11 +222,11 @@ impl Canvas { })); } - pub fn on_mouse_scroll(&mut self, mut handler: F) + pub fn on_mouse_wheel(&mut self, mut handler: F) where F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState), { - self.on_mouse_scroll = Some(self.add_event("wheel", move |event: WheelEvent| { + self.on_mouse_wheel = Some(self.add_event("wheel", move |event: WheelEvent| { if let Some(delta) = event::mouse_scroll_delta(&event) { handler(0, delta, event::mouse_modifiers(&event)); } diff --git a/src/platform_impl/web/web_sys/event.rs b/src/platform_impl/web/web_sys/event.rs index 876fbc22..af557b99 100644 --- a/src/platform_impl/web/web_sys/event.rs +++ b/src/platform_impl/web/web_sys/event.rs @@ -220,5 +220,8 @@ pub fn keyboard_modifiers(event: &KeyboardEvent) -> ModifiersState { } pub fn codepoint(event: &KeyboardEvent) -> char { + // `event.key()` always returns a non-empty `String`. Therefore, this should + // never panic. + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key event.key().chars().next().unwrap() } diff --git a/src/platform_impl/web_sys/event_loop.rs b/src/platform_impl/web_sys/event_loop.rs deleted file mode 100644 index 9a6918a9..00000000 --- a/src/platform_impl/web_sys/event_loop.rs +++ /dev/null @@ -1,378 +0,0 @@ -use super::*; - -use dpi::LogicalPosition; -use event::{ - DeviceId as RootDI, ElementState, Event, KeyboardInput, MouseScrollDelta, StartCause, - TouchPhase, WindowEvent, -}; -use event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW}; -use platform_impl::platform::document; -use std::cell::RefCell; -use std::collections::vec_deque::IntoIter as VecDequeIter; -use std::collections::VecDeque; -use std::marker::PhantomData; -use std::rc::Rc; -use wasm_bindgen::{prelude::*, JsCast}; -use web_sys::{ - EventTarget, FocusEvent, HtmlCanvasElement, KeyboardEvent, PointerEvent, WheelEvent, -}; -use window::WindowId as RootWI; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(i32); - -impl DeviceId { - pub unsafe fn dummy() -> Self { - DeviceId(0) - } -} - -pub struct EventLoop { - elw: RootELW, -} - -pub struct EventLoopWindowTarget { - pub(crate) runner: EventLoopRunnerShared, -} - -impl EventLoopWindowTarget { - fn new() -> Self { - EventLoopWindowTarget { - runner: Rc::new(ELRShared { - runner: RefCell::new(None), - events: RefCell::new(VecDeque::new()), - }), - } - } -} - -#[derive(Clone)] -pub struct EventLoopProxy { - runner: EventLoopRunnerShared, -} - -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.runner.send_event(Event::UserEvent(event)); - Ok(()) - } -} - -pub type EventLoopRunnerShared = Rc>; - -pub struct ELRShared { - runner: RefCell>>, - events: RefCell>>, // TODO: this may not be necessary? -} - -struct EventLoopRunner { - control: ControlFlow, - is_busy: bool, - event_handler: Box, &mut ControlFlow)>, -} - -impl EventLoop { - pub fn new() -> Self { - EventLoop { - elw: RootELW { - p: EventLoopWindowTarget::new(), - _marker: PhantomData, - }, - } - } - - pub fn available_monitors(&self) -> VecDequeIter { - VecDeque::new().into_iter() - } - - pub fn primary_monitor(&self) -> MonitorHandle { - MonitorHandle - } - - pub fn run(self, mut event_handler: F) -> ! - where - F: 'static + FnMut(Event, &RootELW, &mut ControlFlow), - { - let runner = self.elw.p.runner; - - let relw = RootELW { - p: EventLoopWindowTarget::new(), - _marker: PhantomData, - }; - runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl))); - - let document = &document(); - add_event(&runner, document, "blur", |elrs, _: FocusEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::Focused(false), - }); - }); - add_event(&runner, document, "focus", |elrs, _: FocusEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::Focused(true), - }); - }); - add_event( - &runner, - document, - "keydown", - |elrs, event: KeyboardEvent| { - let key = event.key(); - let mut characters = key.chars(); - let first = characters.next(); - let second = characters.next(); - if let (Some(key), None) = (first, second) { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::ReceivedCharacter(key), - }); - } - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::KeyboardInput { - device_id: RootDI(unsafe { DeviceId::dummy() }), - input: KeyboardInput { - scancode: scancode(&event), - state: ElementState::Pressed, - virtual_keycode: button_mapping(&event), - modifiers: keyboard_modifiers_state(&event), - }, - }, - }); - }, - ); - add_event(&runner, document, "keyup", |elrs, event: KeyboardEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::KeyboardInput { - device_id: RootDI(unsafe { DeviceId::dummy() }), - input: KeyboardInput { - scancode: scancode(&event), - state: ElementState::Released, - virtual_keycode: button_mapping(&event), - modifiers: keyboard_modifiers_state(&event), - }, - }, - }); - }); - - runner.send_event(Event::NewEvents(StartCause::Init)); - - // Throw an exception to break out of Rust exceution and use unreachable to tell the - // compiler this function won't return, giving it a return type of '!' - wasm_bindgen::throw_str( - "Using exceptions for control flow, don't mind me. This isn't actually an error!", - ); - } - - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - runner: self.elw.p.runner.clone(), - } - } - - pub fn window_target(&self) -> &RootELW { - &self.elw - } -} - -pub fn register(elrs: &EventLoopRunnerShared, canvas: &HtmlCanvasElement) { - add_event(elrs, canvas, "pointerout", |elrs, event: PointerEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorLeft { - device_id: RootDI(DeviceId(event.pointer_id())), - }, - }); - }); - add_event(elrs, canvas, "pointerover", |elrs, event: PointerEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorEntered { - device_id: RootDI(DeviceId(event.pointer_id())), - }, - }); - }); - add_event(elrs, canvas, "pointermove", |elrs, event: PointerEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorMoved { - device_id: RootDI(DeviceId(event.pointer_id())), - position: LogicalPosition { - x: event.offset_x().into(), - y: event.offset_y().into(), - }, - modifiers: mouse_modifiers_state(&event), - }, - }); - }); - add_event(elrs, canvas, "pointerup", |elrs, event: PointerEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::MouseInput { - device_id: RootDI(DeviceId(event.pointer_id())), - state: ElementState::Pressed, - button: mouse_button(&event), - modifiers: mouse_modifiers_state(&event), - }, - }); - }); - add_event(elrs, canvas, "pointerdown", |elrs, event: PointerEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::MouseInput { - device_id: RootDI(DeviceId(event.pointer_id())), - state: ElementState::Released, - button: mouse_button(&event), - modifiers: mouse_modifiers_state(&event), - }, - }); - }); - add_event(elrs, canvas, "wheel", |elrs, event: WheelEvent| { - let x = event.delta_x(); - let y = event.delta_y(); - let delta = match event.delta_mode() { - WheelEvent::DOM_DELTA_LINE => MouseScrollDelta::LineDelta(x as f32, y as f32), - WheelEvent::DOM_DELTA_PIXEL => MouseScrollDelta::PixelDelta(LogicalPosition { x, y }), - _ => return, - }; - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::MouseWheel { - device_id: RootDI(DeviceId(0)), - delta, - phase: TouchPhase::Moved, - modifiers: mouse_modifiers_state(&event), - }, - }); - }); -} - -fn add_event( - elrs: &EventLoopRunnerShared, - target: &EventTarget, - event: &str, - mut handler: F, -) where - E: AsRef + wasm_bindgen::convert::FromWasmAbi + 'static, - F: FnMut(&EventLoopRunnerShared, E) + 'static, -{ - let elrs = elrs.clone(); - - let closure = Closure::wrap(Box::new(move |event: E| { - // Don't capture the event if the events loop has been destroyed - match &*elrs.runner.borrow() { - Some(ref runner) if runner.control == ControlFlow::Exit => return, - _ => (), - } - - { - let event_ref = event.as_ref(); - event_ref.prevent_default(); - event_ref.stop_propagation(); - event_ref.cancel_bubble(); - } - - handler(&elrs, event); - }) as Box); - - target.add_event_listener_with_callback(event, &closure.as_ref().unchecked_ref()); - closure.forget(); // TODO: don't leak this. -} - -impl ELRShared { - // Set the event callback to use for the event loop runner - // This the event callback is a fairly thin layer over the user-provided callback that closes - // over a RootEventLoopWindowTarget reference - fn set_listener(&self, event_handler: Box, &mut ControlFlow)>) { - *self.runner.borrow_mut() = Some(EventLoopRunner { - control: ControlFlow::Poll, - is_busy: false, - event_handler, - }); - } - - // Add an event to the event loop runner - // - // It will determine if the event should be immediately sent to the user or buffered for later - pub fn send_event(&self, event: Event) { - // If the event loop is closed, it should discard any new events - if self.closed() { - return; - } - - let start_cause = StartCause::Poll; // TODO: determine start cause - - // Determine if event handling is in process, and then release the borrow on the runner - let is_busy = if let Some(ref runner) = *self.runner.borrow() { - runner.is_busy - } else { - true // If there is no event runner yet, then there's no point in processing events - }; - - if is_busy { - self.events.borrow_mut().push_back(event); - } else { - // Handle starting a new batch of events - // - // The user is informed via Event::NewEvents that there is a batch of events to process - // However, there is only one of these per batch of events - self.handle_event(Event::NewEvents(start_cause)); - self.handle_event(event); - self.handle_event(Event::EventsCleared); - - // If the event loop is closed, it has been closed this iteration and now the closing - // event should be emitted - if self.closed() { - self.handle_event(Event::LoopDestroyed); - } - } - } - - // Check if the event loop is currntly closed - fn closed(&self) -> bool { - match *self.runner.borrow() { - Some(ref runner) => runner.control == ControlFlow::Exit, - None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed - } - } - - // handle_event takes in events and either queues them or applies a callback - // - // It should only ever be called from send_event - fn handle_event(&self, event: Event) { - let closed = self.closed(); - - match *self.runner.borrow_mut() { - Some(ref mut runner) => { - // An event is being processed, so the runner should be marked busy - runner.is_busy = true; - - // TODO: bracket this in control flow events? - (runner.event_handler)(event, &mut runner.control); - - // Maintain closed state, even if the callback changes it - if closed { - runner.control = ControlFlow::Exit; - } - - // An event is no longer being processed - runner.is_busy = false; - } - // If an event is being handled without a runner somehow, add it to the event queue so - // it will eventually be processed - _ => self.events.borrow_mut().push_back(event), - } - - // Don't take events out of the queue if the loop is closed or the runner doesn't exist - // If the runner doesn't exist and this method recurses, it will recurse infinitely - if !closed && self.runner.borrow().is_some() { - // Take an event out of the queue and handle it - if let Some(event) = self.events.borrow_mut().pop_front() { - self.handle_event(event); - } - } - } -} diff --git a/src/platform_impl/web_sys/events.rs b/src/platform_impl/web_sys/events.rs deleted file mode 100644 index 4e13ca2a..00000000 --- a/src/platform_impl/web_sys/events.rs +++ /dev/null @@ -1,201 +0,0 @@ -use std::convert::TryInto; - -use web_sys::{KeyboardEvent, MouseEvent}; -use event::{MouseButton, ModifiersState, ScanCode, VirtualKeyCode}; - -pub fn button_mapping(event: &KeyboardEvent) -> Option { - Some(match &event.code()[..] { - "Digit1" => VirtualKeyCode::Key1, - "Digit2" => VirtualKeyCode::Key2, - "Digit3" => VirtualKeyCode::Key3, - "Digit4" => VirtualKeyCode::Key4, - "Digit5" => VirtualKeyCode::Key5, - "Digit6" => VirtualKeyCode::Key6, - "Digit7" => VirtualKeyCode::Key7, - "Digit8" => VirtualKeyCode::Key8, - "Digit9" => VirtualKeyCode::Key9, - "Digit0" => VirtualKeyCode::Key0, - "KeyA" => VirtualKeyCode::A, - "KeyB" => VirtualKeyCode::B, - "KeyC" => VirtualKeyCode::C, - "KeyD" => VirtualKeyCode::D, - "KeyE" => VirtualKeyCode::E, - "KeyF" => VirtualKeyCode::F, - "KeyG" => VirtualKeyCode::G, - "KeyH" => VirtualKeyCode::H, - "KeyI" => VirtualKeyCode::I, - "KeyJ" => VirtualKeyCode::J, - "KeyK" => VirtualKeyCode::K, - "KeyL" => VirtualKeyCode::L, - "KeyM" => VirtualKeyCode::M, - "KeyN" => VirtualKeyCode::N, - "KeyO" => VirtualKeyCode::O, - "KeyP" => VirtualKeyCode::P, - "KeyQ" => VirtualKeyCode::Q, - "KeyR" => VirtualKeyCode::R, - "KeyS" => VirtualKeyCode::S, - "KeyT" => VirtualKeyCode::T, - "KeyU" => VirtualKeyCode::U, - "KeyV" => VirtualKeyCode::V, - "KeyW" => VirtualKeyCode::W, - "KeyX" => VirtualKeyCode::X, - "KeyY" => VirtualKeyCode::Y, - "KeyZ" => VirtualKeyCode::Z, - "Escape" => VirtualKeyCode::Escape, - "F1" => VirtualKeyCode::F1, - "F2" => VirtualKeyCode::F2, - "F3" => VirtualKeyCode::F3, - "F4" => VirtualKeyCode::F4, - "F5" => VirtualKeyCode::F5, - "F6" => VirtualKeyCode::F6, - "F7" => VirtualKeyCode::F7, - "F8" => VirtualKeyCode::F8, - "F9" => VirtualKeyCode::F9, - "F10" => VirtualKeyCode::F10, - "F11" => VirtualKeyCode::F11, - "F12" => VirtualKeyCode::F12, - "F13" => VirtualKeyCode::F13, - "F14" => VirtualKeyCode::F14, - "F15" => VirtualKeyCode::F15, - "F16" => VirtualKeyCode::F16, - "F17" => VirtualKeyCode::F17, - "F18" => VirtualKeyCode::F18, - "F19" => VirtualKeyCode::F19, - "F20" => VirtualKeyCode::F20, - "F21" => VirtualKeyCode::F21, - "F22" => VirtualKeyCode::F22, - "F23" => VirtualKeyCode::F23, - "F24" => VirtualKeyCode::F24, - "PrintScreen" => VirtualKeyCode::Snapshot, - "ScrollLock" => VirtualKeyCode::Scroll, - "Pause" => VirtualKeyCode::Pause, - "Insert" => VirtualKeyCode::Insert, - "Home" => VirtualKeyCode::Home, - "Delete" => VirtualKeyCode::Delete, - "End" => VirtualKeyCode::End, - "PageDown" => VirtualKeyCode::PageDown, - "PageUp" => VirtualKeyCode::PageUp, - "ArrowLeft" => VirtualKeyCode::Left, - "ArrowUp" => VirtualKeyCode::Up, - "ArrowRight" => VirtualKeyCode::Right, - "ArrowDown" => VirtualKeyCode::Down, - "Backspace" => VirtualKeyCode::Back, - "Enter" => VirtualKeyCode::Return, - "Space" => VirtualKeyCode::Space, - "Compose" => VirtualKeyCode::Compose, - "Caret" => VirtualKeyCode::Caret, - "NumLock" => VirtualKeyCode::Numlock, - "Numpad0" => VirtualKeyCode::Numpad0, - "Numpad1" => VirtualKeyCode::Numpad1, - "Numpad2" => VirtualKeyCode::Numpad2, - "Numpad3" => VirtualKeyCode::Numpad3, - "Numpad4" => VirtualKeyCode::Numpad4, - "Numpad5" => VirtualKeyCode::Numpad5, - "Numpad6" => VirtualKeyCode::Numpad6, - "Numpad7" => VirtualKeyCode::Numpad7, - "Numpad8" => VirtualKeyCode::Numpad8, - "Numpad9" => VirtualKeyCode::Numpad9, - "AbntC1" => VirtualKeyCode::AbntC1, - "AbntC2" => VirtualKeyCode::AbntC2, - "NumpadAdd" => VirtualKeyCode::Add, - "Quote" => VirtualKeyCode::Apostrophe, - "Apps" => VirtualKeyCode::Apps, - "At" => VirtualKeyCode::At, - "Ax" => VirtualKeyCode::Ax, - "Backslash" => VirtualKeyCode::Backslash, - "Calculator" => VirtualKeyCode::Calculator, - "Capital" => VirtualKeyCode::Capital, - "Semicolon" => VirtualKeyCode::Semicolon, - "Comma" => VirtualKeyCode::Comma, - "Convert" => VirtualKeyCode::Convert, - "NumpadDecimal" => VirtualKeyCode::Decimal, - "NumpadDivide" => VirtualKeyCode::Divide, - "Equal" => VirtualKeyCode::Equals, - "Backquote" => VirtualKeyCode::Grave, - "Kana" => VirtualKeyCode::Kana, - "Kanji" => VirtualKeyCode::Kanji, - "AltLeft" => VirtualKeyCode::LAlt, - "BracketLeft" => VirtualKeyCode::LBracket, - "ControlLeft" => VirtualKeyCode::LControl, - "ShiftLeft" => VirtualKeyCode::LShift, - "MetaLeft" => VirtualKeyCode::LWin, - "Mail" => VirtualKeyCode::Mail, - "MediaSelect" => VirtualKeyCode::MediaSelect, - "MediaStop" => VirtualKeyCode::MediaStop, - "Minus" => VirtualKeyCode::Minus, - "NumpadMultiply" => VirtualKeyCode::Multiply, - "Mute" => VirtualKeyCode::Mute, - "LaunchMyComputer" => VirtualKeyCode::MyComputer, - "NavigateForward" => VirtualKeyCode::NavigateForward, - "NavigateBackward" => VirtualKeyCode::NavigateBackward, - "NextTrack" => VirtualKeyCode::NextTrack, - "NoConvert" => VirtualKeyCode::NoConvert, - "NumpadComma" => VirtualKeyCode::NumpadComma, - "NumpadEnter" => VirtualKeyCode::NumpadEnter, - "NumpadEquals" => VirtualKeyCode::NumpadEquals, - "OEM102" => VirtualKeyCode::OEM102, - "Period" => VirtualKeyCode::Period, - "PlayPause" => VirtualKeyCode::PlayPause, - "Power" => VirtualKeyCode::Power, - "PrevTrack" => VirtualKeyCode::PrevTrack, - "AltRight" => VirtualKeyCode::RAlt, - "BracketRight" => VirtualKeyCode::RBracket, - "ControlRight" => VirtualKeyCode::RControl, - "ShiftRight" => VirtualKeyCode::RShift, - "MetaRight" => VirtualKeyCode::RWin, - "Slash" => VirtualKeyCode::Slash, - "Sleep" => VirtualKeyCode::Sleep, - "Stop" => VirtualKeyCode::Stop, - "NumpadSubtract" => VirtualKeyCode::Subtract, - "Sysrq" => VirtualKeyCode::Sysrq, - "Tab" => VirtualKeyCode::Tab, - "Underline" => VirtualKeyCode::Underline, - "Unlabeled" => VirtualKeyCode::Unlabeled, - "AudioVolumeDown" => VirtualKeyCode::VolumeDown, - "AudioVolumeUp" => VirtualKeyCode::VolumeUp, - "Wake" => VirtualKeyCode::Wake, - "WebBack" => VirtualKeyCode::WebBack, - "WebFavorites" => VirtualKeyCode::WebFavorites, - "WebForward" => VirtualKeyCode::WebForward, - "WebHome" => VirtualKeyCode::WebHome, - "WebRefresh" => VirtualKeyCode::WebRefresh, - "WebSearch" => VirtualKeyCode::WebSearch, - "WebStop" => VirtualKeyCode::WebStop, - "Yen" => VirtualKeyCode::Yen, - _ => return None - }) -} - -pub fn mouse_modifiers_state(event: &MouseEvent) -> ModifiersState { - ModifiersState { - shift: event.shift_key(), - ctrl: event.ctrl_key(), - alt: event.alt_key(), - logo: event.meta_key(), - } -} - -pub fn mouse_button(event: &MouseEvent) -> MouseButton { - match event.button() { - 0 => MouseButton::Left, - 1 => MouseButton::Middle, - 2 => MouseButton::Right, - i => MouseButton::Other((i - 3).try_into().expect("very large mouse button value")), - } -} - -pub fn keyboard_modifiers_state(event: &KeyboardEvent) -> ModifiersState { - ModifiersState { - shift: event.shift_key(), - ctrl: event.ctrl_key(), - alt: event.alt_key(), - logo: event.meta_key(), - } -} - -pub fn scancode(event: &KeyboardEvent) -> ScanCode { - match event.key_code() { - 0 => event.char_code(), - i => i, - } -} diff --git a/src/platform_impl/web_sys/mod.rs b/src/platform_impl/web_sys/mod.rs deleted file mode 100644 index 1b0591b1..00000000 --- a/src/platform_impl/web_sys/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::fmt; - -mod event_loop; -mod events; -mod window; - -pub use self::event_loop::{ - register, DeviceId, EventLoop, EventLoopProxy, EventLoopRunnerShared, EventLoopWindowTarget, -}; -pub use self::events::{ - button_mapping, keyboard_modifiers_state, mouse_button, mouse_modifiers_state, scancode, -}; -pub use self::window::{MonitorHandle, PlatformSpecificWindowBuilderAttributes, Window, WindowId}; - -#[derive(Debug)] -pub struct OsError(String); - -impl fmt::Display for OsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -fn window() -> web_sys::Window { - web_sys::window().unwrap() -} - -fn document() -> web_sys::Document { - window().document().unwrap() -} diff --git a/src/platform_impl/web_sys/window.rs b/src/platform_impl/web_sys/window.rs deleted file mode 100644 index bd51c3f3..00000000 --- a/src/platform_impl/web_sys/window.rs +++ /dev/null @@ -1,330 +0,0 @@ -use super::{register, EventLoopWindowTarget, OsError}; -use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; -use error::{ExternalError, NotSupportedError, OsError as RootOE}; -use event::{Event, WindowEvent}; -use icon::Icon; -use monitor::MonitorHandle as RootMH; -use platform::web_sys::WindowExtWebSys; -use platform_impl::platform::{document, window}; -use std::cell::RefCell; -use std::collections::vec_deque::IntoIter as VecDequeIter; -use std::collections::VecDeque; -use wasm_bindgen::{closure::Closure, JsCast}; -use web_sys::HtmlCanvasElement; -use window::{CursorIcon, Window as RootWindow, WindowAttributes, WindowId as RootWI}; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct MonitorHandle; - -impl MonitorHandle { - pub fn hidpi_factor(&self) -> f64 { - 1.0 - } - - pub fn position(&self) -> PhysicalPosition { - unimplemented!(); - } - - pub fn dimensions(&self) -> PhysicalSize { - unimplemented!(); - } - - pub fn name(&self) -> Option { - unimplemented!(); - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId; - -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PlatformSpecificWindowBuilderAttributes; - -impl WindowId { - pub unsafe fn dummy() -> WindowId { - WindowId - } -} - -pub struct Window { - pub(crate) canvas: HtmlCanvasElement, - pub(crate) redraw: Box, - previous_pointer: RefCell<&'static str>, - position: RefCell, -} - -impl Window { - pub fn new( - target: &EventLoopWindowTarget, - attr: WindowAttributes, - _: PlatformSpecificWindowBuilderAttributes, - ) -> Result { - let element = document() - .create_element("canvas") - .map_err(|_| os_error!(OsError("Failed to create canvas element".to_owned())))?; - let canvas: HtmlCanvasElement = element.unchecked_into(); - document() - .body() - .ok_or_else(|| os_error!(OsError("Failed to find body node".to_owned())))? - .append_child(&canvas) - .map_err(|_| os_error!(OsError("Failed to append canvas".to_owned())))?; - - register(&target.runner, &canvas); - - let runner = target.runner.clone(); - let redraw = Box::new(move || { - let runner = runner.clone(); - let closure = Closure::once_into_js(move |_: f64| { - runner.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::RedrawRequested, - }); - }); - window().request_animation_frame(closure.as_ref().unchecked_ref()); - }); - - let window = Window { - canvas, - redraw, - previous_pointer: RefCell::new("auto"), - position: RefCell::new(LogicalPosition { x: 0.0, y: 0.0 }), - }; - - if let Some(inner_size) = attr.inner_size { - window.set_inner_size(inner_size); - } else { - window.set_inner_size(LogicalSize { - width: 1024.0, - height: 768.0, - }) - } - window.set_min_inner_size(attr.min_inner_size); - window.set_max_inner_size(attr.max_inner_size); - window.set_resizable(attr.resizable); - window.set_title(&attr.title); - window.set_maximized(attr.maximized); - window.set_visible(attr.visible); - //window.set_transparent(attr.transparent); - window.set_decorations(attr.decorations); - window.set_always_on_top(attr.always_on_top); - window.set_window_icon(attr.window_icon); - - Ok(window) - } - - pub fn set_title(&self, title: &str) { - document().set_title(title); - } - - pub fn set_visible(&self, _visible: bool) { - // Intentionally a no-op - } - - pub fn request_redraw(&self) { - (self.redraw)(); - } - - pub fn outer_position(&self) -> Result { - let bounds = self.canvas.get_bounding_client_rect(); - Ok(LogicalPosition { - x: bounds.x(), - y: bounds.y(), - }) - } - - pub fn inner_position(&self) -> Result { - Ok(*self.position.borrow()) - } - - pub fn set_outer_position(&self, position: LogicalPosition) { - *self.position.borrow_mut() = position; - self.canvas - .set_attribute("position", "fixed") - .expect("Setting the position for the canvas"); - self.canvas - .set_attribute("left", &position.x.to_string()) - .expect("Setting the position for the canvas"); - self.canvas - .set_attribute("top", &position.y.to_string()) - .expect("Setting the position for the canvas"); - } - - #[inline] - pub fn inner_size(&self) -> LogicalSize { - LogicalSize { - width: self.canvas.width() as f64, - height: self.canvas.height() as f64, - } - } - - #[inline] - pub fn outer_size(&self) -> LogicalSize { - LogicalSize { - width: self.canvas.width() as f64, - height: self.canvas.height() as f64, - } - } - - #[inline] - pub fn set_inner_size(&self, size: LogicalSize) { - self.canvas.set_width(size.width as u32); - self.canvas.set_height(size.height as u32); - } - - #[inline] - pub fn set_min_inner_size(&self, _dimensions: Option) { - // Intentionally a no-op: users can't resize canvas elements - } - - #[inline] - pub fn set_max_inner_size(&self, _dimensions: Option) { - // Intentionally a no-op: users can't resize canvas elements - } - - #[inline] - pub fn set_resizable(&self, _resizable: bool) { - // Intentionally a no-op: users can't resize canvas elements - } - - #[inline] - pub fn hidpi_factor(&self) -> f64 { - 1.0 - } - - #[inline] - pub fn set_cursor_icon(&self, cursor: CursorIcon) { - let text = match cursor { - CursorIcon::Default => "auto", - CursorIcon::Crosshair => "crosshair", - CursorIcon::Hand => "pointer", - CursorIcon::Arrow => "default", - CursorIcon::Move => "move", - CursorIcon::Text => "text", - CursorIcon::Wait => "wait", - CursorIcon::Help => "help", - CursorIcon::Progress => "progress", - - CursorIcon::NotAllowed => "not-allowed", - CursorIcon::ContextMenu => "context-menu", - CursorIcon::Cell => "cell", - CursorIcon::VerticalText => "vertical-text", - CursorIcon::Alias => "alias", - CursorIcon::Copy => "copy", - CursorIcon::NoDrop => "no-drop", - CursorIcon::Grab => "grab", - CursorIcon::Grabbing => "grabbing", - CursorIcon::AllScroll => "all-scroll", - CursorIcon::ZoomIn => "zoom-in", - CursorIcon::ZoomOut => "zoom-out", - - CursorIcon::EResize => "e-resize", - CursorIcon::NResize => "n-resize", - CursorIcon::NeResize => "ne-resize", - CursorIcon::NwResize => "nw-resize", - CursorIcon::SResize => "s-resize", - CursorIcon::SeResize => "se-resize", - CursorIcon::SwResize => "sw-resize", - CursorIcon::WResize => "w-resize", - CursorIcon::EwResize => "ew-resize", - CursorIcon::NsResize => "ns-resize", - CursorIcon::NeswResize => "nesw-resize", - CursorIcon::NwseResize => "nwse-resize", - CursorIcon::ColResize => "col-resize", - CursorIcon::RowResize => "row-resize", - }; - *self.previous_pointer.borrow_mut() = text; - self.canvas - .set_attribute("cursor", text) - .expect("Setting the cursor on the canvas"); - } - - #[inline] - pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), ExternalError> { - // TODO: pointer capture - Ok(()) - } - - #[inline] - pub fn set_cursor_grab(&self, _grab: bool) -> Result<(), ExternalError> { - // TODO: pointer capture - Ok(()) - } - - #[inline] - pub fn set_cursor_visible(&self, visible: bool) { - if !visible { - self.canvas - .set_attribute("cursor", "none") - .expect("Setting the cursor on the canvas"); - } else { - self.canvas - .set_attribute("cursor", *self.previous_pointer.borrow()) - .expect("Setting the cursor on the canvas"); - } - } - - #[inline] - pub fn set_maximized(&self, _maximized: bool) { - // TODO: should there be a maximization / fullscreen API? - } - - #[inline] - pub fn fullscreen(&self) -> Option { - // TODO: should there be a maximization / fullscreen API? - None - } - - #[inline] - pub fn set_fullscreen(&self, _monitor: Option) { - // TODO: should there be a maximization / fullscreen API? - } - - #[inline] - pub fn set_decorations(&self, _decorations: bool) { - // Intentionally a no-op, no canvas decorations - } - - #[inline] - pub fn set_always_on_top(&self, _always_on_top: bool) { - // Intentionally a no-op, no window ordering - } - - #[inline] - pub fn set_window_icon(&self, _window_icon: Option) { - // Currently an intentional no-op - } - - #[inline] - pub fn set_ime_position(&self, _position: LogicalPosition) { - // TODO: what is this? - } - - #[inline] - pub fn current_monitor(&self) -> RootMH { - RootMH { - inner: MonitorHandle, - } - } - - #[inline] - pub fn available_monitors(&self) -> VecDequeIter { - VecDeque::new().into_iter() - } - - #[inline] - pub fn primary_monitor(&self) -> MonitorHandle { - MonitorHandle - } - - #[inline] - pub fn id(&self) -> WindowId { - // TODO ? - unsafe { WindowId::dummy() } - } -} - -impl WindowExtWebSys for RootWindow { - fn canvas(&self) -> HtmlCanvasElement { - self.window.canvas.clone() - } -}