From fd4db4000c7b68d736906870d7fb157835b1d67f Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Tue, 12 Feb 2019 20:47:31 -0500 Subject: [PATCH 01/27] Create the type layout Everything typechecks, but nothing is implemented --- Cargo.toml | 4 + src/platform_impl/mod.rs | 6 +- src/platform_impl/stdweb/mod.rs | 245 ++++++++++++++++++++++++++++++++ 3 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 src/platform_impl/stdweb/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 7017dc9515..baa0d4ef1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,3 +72,7 @@ percent-encoding = "1.0" [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "windows"))'.dependencies.parking_lot] version = "0.7" + +[target.'cfg(target_arch = "wasm32")'.dependencies.stdweb] +version = "0.4.13" +optional = true diff --git a/src/platform_impl/mod.rs b/src/platform_impl/mod.rs index be96878500..2a8ccd62d4 100644 --- a/src/platform_impl/mod.rs +++ b/src/platform_impl/mod.rs @@ -18,9 +18,13 @@ mod platform; #[cfg(target_os = "emscripten")] #[path="emscripten/mod.rs"] mod platform; +#[cfg(feature = "stdweb")] +#[path="stdweb/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")))] + not(target_os = "emscripten"), + not(feature = "stdweb")))] compile_error!("The platform you're compiling for is not supported by winit"); diff --git a/src/platform_impl/stdweb/mod.rs b/src/platform_impl/stdweb/mod.rs new file mode 100644 index 0000000000..7d9d3cfeaf --- /dev/null +++ b/src/platform_impl/stdweb/mod.rs @@ -0,0 +1,245 @@ +use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; +use event::Event; +use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; +use icon::Icon; +use monitor::{MonitorHandle as RootMH}; +use window::{CreationError, MouseCursor, WindowAttributes}; + +use std::collections::vec_deque::IntoIter as VecDequeIter; +use std::marker::PhantomData; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DeviceId; + +impl DeviceId { + pub unsafe fn dummy() -> Self { + DeviceId + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MonitorHandle; + +impl MonitorHandle { + pub fn get_hidpi_factor(&self) -> f64 { + unimplemented!(); + } + + pub fn get_position(&self) -> PhysicalPosition { + unimplemented!(); + } + + pub fn get_dimensions(&self) -> PhysicalSize { + unimplemented!(); + } + + pub fn get_name(&self) -> Option { + unimplemented!(); + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct WindowId; + +impl WindowId { + pub unsafe fn dummy() -> WindowId { + WindowId + } +} + +pub struct Window; + +impl Window { + // TODO: type of window_target + pub fn new(target: &EventLoopWindowTarget, window: WindowAttributes, platform: PlatformSpecificWindowBuilderAttributes) -> Result { + unimplemented!(); + } + + pub fn set_title(&self, title: &str) { + unimplemented!(); + } + + pub fn show(&self) { + unimplemented!(); + } + + pub fn hide(&self) { + unimplemented!(); + } + + pub fn request_redraw(&self) { + unimplemented!(); + } + + pub fn get_position(&self) -> Option { + unimplemented!(); + } + + pub fn get_inner_position(&self) -> Option { + unimplemented!(); + } + + pub fn set_position(&self, position: LogicalPosition) { + unimplemented!(); + } + + #[inline] + pub fn get_inner_size(&self) -> Option { + unimplemented!(); + } + + #[inline] + pub fn get_outer_size(&self) -> Option { + unimplemented!(); + } + + #[inline] + pub fn set_inner_size(&self, size: LogicalSize) { + unimplemented!(); + } + + #[inline] + pub fn set_min_dimensions(&self, dimensions: Option) { + unimplemented!(); + } + + #[inline] + pub fn set_max_dimensions(&self, dimensions: Option) { + unimplemented!(); + } + + #[inline] + pub fn set_resizable(&self, resizable: bool) { + unimplemented!(); + } + + #[inline] + pub fn get_hidpi_factor(&self) -> f64 { + unimplemented!(); + } + + #[inline] + pub fn set_cursor(&self, cursor: MouseCursor) { + unimplemented!(); + } + + #[inline] + pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> { + unimplemented!(); + } + + #[inline] + pub fn grab_cursor(&self, grab: bool) -> Result<(), String> { + unimplemented!(); + } + + #[inline] + pub fn hide_cursor(&self, hide: bool) { + unimplemented!(); + } + + #[inline] + pub fn set_maximized(&self, maximized: bool) { + unimplemented!(); + } + + #[inline] + pub fn set_fullscreen(&self, monitor: Option) { + unimplemented!(); + } + + #[inline] + pub fn set_decorations(&self, decorations: bool) { + unimplemented!(); + } + + #[inline] + pub fn set_always_on_top(&self, always_on_top: bool) { + unimplemented!(); + } + + #[inline] + pub fn set_window_icon(&self, window_icon: Option) { + unimplemented!(); + } + + #[inline] + pub fn set_ime_spot(&self, position: LogicalPosition) { + unimplemented!(); + } + + #[inline] + pub fn get_current_monitor(&self) -> RootMH { + unimplemented!(); + } + + #[inline] + pub fn get_available_monitors(&self) -> VecDequeIter { + unimplemented!(); + } + + #[inline] + pub fn get_primary_monitor(&self) -> MonitorHandle { + unimplemented!(); + } + + #[inline] + pub fn id(&self) -> WindowId { + unimplemented!(); + } +} + +pub struct EventLoop { + _phantom: PhantomData +} + +impl EventLoop { + pub fn new() -> Self { + unimplemented!(); + } + + pub fn get_available_monitors(&self) -> VecDequeIter { + unimplemented!(); + } + + pub fn get_primary_monitor(&self) -> MonitorHandle { + unimplemented!(); + } + + pub fn run(mut self, event_handler: F) -> ! + where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) + { + unimplemented!(); + } + + pub fn create_proxy(&self) -> EventLoopProxy { + unimplemented!(); + } + + pub fn window_target(&self) -> &RootELW { + unimplemented!(); + /*&EventLoopWindowTarget { + p: self.event_loop.window_target(), + _marker: std::marker::PhantomData + }*/ + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct EventLoopProxy { + _phantom: PhantomData +} + +impl EventLoopProxy { + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + unimplemented!(); + } +} + +pub struct EventLoopWindowTarget { + _phantom: PhantomData +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PlatformSpecificWindowBuilderAttributes; + From f44e98ddc92d1faf5b1a5d53f7e27cb650296817 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Tue, 19 Feb 2019 20:08:18 -0500 Subject: [PATCH 02/27] Implemented a few easy methods --- src/platform_impl/stdweb/mod.rs | 163 ++++++++++++++++++++++++-------- 1 file changed, 126 insertions(+), 37 deletions(-) diff --git a/src/platform_impl/stdweb/mod.rs b/src/platform_impl/stdweb/mod.rs index 7d9d3cfeaf..4f8f6a4507 100644 --- a/src/platform_impl/stdweb/mod.rs +++ b/src/platform_impl/stdweb/mod.rs @@ -4,9 +4,19 @@ use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed} use icon::Icon; use monitor::{MonitorHandle as RootMH}; use window::{CreationError, MouseCursor, WindowAttributes}; - +use stdweb::{ + document, + web::html_element::CanvasElement +}; +use std::cell::Cell; +use std::collections::VecDeque; use std::collections::vec_deque::IntoIter as VecDequeIter; use std::marker::PhantomData; +use std::rc::Rc; + +// TODO: dpi +// TODO: pointer locking (stdweb PR required) +// TODO: should there be a maximization / fullscreen API? #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId; @@ -47,7 +57,10 @@ impl WindowId { } } -pub struct Window; +pub struct Window { + canvas: CanvasElement, + monitors: VecDeque +} impl Window { // TODO: type of window_target @@ -56,61 +69,74 @@ impl Window { } pub fn set_title(&self, title: &str) { - unimplemented!(); + document().set_title(title); } pub fn show(&self) { - unimplemented!(); + // Intentionally a no-op } pub fn hide(&self) { - unimplemented!(); + // Intentionally a no-op } pub fn request_redraw(&self) { + // TODO: what does this mean unimplemented!(); } pub fn get_position(&self) -> Option { - unimplemented!(); + let bounds = canvas.get_bouding_client_rect(); + Some(LogicalPosition { + x: bounds.get_x(), + y: bounds.get_y(), + }) } pub fn get_inner_position(&self) -> Option { - unimplemented!(); + self.get_inner_position() } pub fn set_position(&self, position: LogicalPosition) { + // TODO: use CSS? unimplemented!(); } #[inline] pub fn get_inner_size(&self) -> Option { - unimplemented!(); + Some(LogicalSize { + x: self.canvas.width() as f64 + y: self.canvas.height() as f64 + }) } #[inline] pub fn get_outer_size(&self) -> Option { - unimplemented!(); + Some(LogicalSize { + x: self.canvas.width() as f64 + y: self.canvas.height() as f64 + }) } #[inline] pub fn set_inner_size(&self, size: LogicalSize) { - unimplemented!(); + self.canvas.set_width(size.x as u32); + self.canvas.set_height(size.y as u32); } #[inline] - pub fn set_min_dimensions(&self, dimensions: Option) { - unimplemented!(); + pub fn set_min_dimensions(&self, _dimensions: Option) { + // Intentionally a no-op: users can't resize canvas elements } #[inline] - pub fn set_max_dimensions(&self, dimensions: Option) { - unimplemented!(); + pub fn set_max_dimensions(&self, _dimensions: Option) { + // Intentionally a no-op: users can't resize canvas elements } #[inline] - pub fn set_resizable(&self, resizable: bool) { - unimplemented!(); + pub fn set_resizable(&self, _resizable: bool) { + // Intentionally a no-op: users can't resize canvas elements } #[inline] @@ -120,46 +146,92 @@ impl Window { #[inline] pub fn set_cursor(&self, cursor: MouseCursor) { - unimplemented!(); + let text = match cursor { + MouseCursor::Default => "auto", + MouseCursor::Crosshair => "crosshair", + MouseCursor::Hand => "pointer", + MouseCursor::Arrow => "default", + MouseCursor::Move => "move", + MouseCursor::Text => "text", + MouseCursor::Wait => "wait", + MouseCursor::Help => "help", + MouseCursor::Progress => "progress", + + MouseCursor::NotAllowed => "not-allowed", + MouseCursor::ContextMenu => "context-menu", + MouseCursor::Cell => "cell", + MouseCursor::VerticalText => "vertical-text", + MouseCursor::Alias => "alias", + MouseCursor::Copy => "copy", + MouseCursor::NoDrop => "no-drop", + MouseCursor::Grab => "grab", + MouseCursor::Grabbing => "grabbing", + MouseCursor::AllScroll => "all-scroll", + MouseCursor::ZoomIn => "zoom-in", + MouseCursor::ZoomOut => "zoom-out", + + MouseCursor::EResize => "e-resize", + MouseCursor::NResize => "n-resize", + MouseCursor::NeResize => "ne-resize", + MouseCursor::NwResize => "nw-resize", + MouseCursor::SResize => "s-resize", + MouseCursor::SeResize => "se-resize", + MouseCursor::SwResize => "sw-resize", + MouseCursor::WResize => "w-resize", + MouseCursor::EwResize => "ew-resize", + MouseCursor::NsResize => "ns-resize", + MouseCursor::NeswResize => "nesw-resize", + MouseCursor::NwseResize => "nwse-resize", + MouseCursor::ColResize => "col-resize", + MouseCursor::RowResize => "row-resize", + }; + self.canvas.set_attribute("cursor", text); + .expect("Setting the cursor on the canvas"); } #[inline] pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> { + // TODO: pointer capture unimplemented!(); } #[inline] pub fn grab_cursor(&self, grab: bool) -> Result<(), String> { + // TODO: pointer capture unimplemented!(); } #[inline] pub fn hide_cursor(&self, hide: bool) { - unimplemented!(); + self.canvas.set_attribute("cursor", "none") + .expect("Setting the cursor on the canvas"); } #[inline] pub fn set_maximized(&self, maximized: bool) { + // TODO: should there be a maximization / fullscreen API? unimplemented!(); } #[inline] pub fn set_fullscreen(&self, monitor: Option) { + // TODO: should there be a maximization / fullscreen API? unimplemented!(); } #[inline] - pub fn set_decorations(&self, decorations: bool) { - unimplemented!(); + 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) { - unimplemented!(); + 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) { + // TODO: should this set the favicon? unimplemented!(); } @@ -170,27 +242,43 @@ impl Window { #[inline] pub fn get_current_monitor(&self) -> RootMH { - unimplemented!(); + RootMH { + inner: MonitorHandle + } } #[inline] pub fn get_available_monitors(&self) -> VecDequeIter { - unimplemented!(); + self.monitors.iter() } #[inline] pub fn get_primary_monitor(&self) -> MonitorHandle { - unimplemented!(); + MonitorHandle } #[inline] pub fn id(&self) -> WindowId { - unimplemented!(); + WindowId::dummy() + } +} + +fn new_rootelw() -> RootELW { + RootELW { + p: EventLoopWindowTarget, + _marker: PhantomData } } pub struct EventLoop { - _phantom: PhantomData + window_target: RootELW, + monitors: VecDeque, + data: Rc>, +} + +struct EventLoopData { + events: VecDeque, + control: ControlFlow, } impl EventLoop { @@ -199,40 +287,41 @@ impl EventLoop { } pub fn get_available_monitors(&self) -> VecDequeIter { - unimplemented!(); + self.monitors.iter() } pub fn get_primary_monitor(&self) -> MonitorHandle { - unimplemented!(); + MonitorHandle } pub fn run(mut self, event_handler: F) -> ! where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { + // TODO: Create event handlers for the JS events + // TODO: how to handle request redraw? unimplemented!(); } pub fn create_proxy(&self) -> EventLoopProxy { - unimplemented!(); + EventLoopProxy { + events: self.window_target.p.events.clone() + } } pub fn window_target(&self) -> &RootELW { - unimplemented!(); - /*&EventLoopWindowTarget { - p: self.event_loop.window_target(), - _marker: std::marker::PhantomData - }*/ + &self.window_target } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct EventLoopProxy { - _phantom: PhantomData + data: EventLoopData } impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - unimplemented!(); + self.data.borrow_mut().events.push_back(Event::UserEvent(event)); + Ok(()) } } From c088f8bd030ac341feda298a67e6c4f1f53f49e3 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Tue, 26 Feb 2019 13:36:48 -0500 Subject: [PATCH 03/27] Create the outline of event input and handler calls --- src/lib.rs | 2 + src/platform_impl/stdweb/mod.rs | 120 +++++++++++++++++++++++++------- 2 files changed, 97 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ff374806ee..4155fa2597 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,8 @@ extern crate parking_lot; extern crate percent_encoding; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] extern crate smithay_client_toolkit as sctk; +#[cfg(feature = "stdweb")] +extern crate stdweb; pub mod dpi; pub mod event; diff --git a/src/platform_impl/stdweb/mod.rs b/src/platform_impl/stdweb/mod.rs index 4f8f6a4507..727011366b 100644 --- a/src/platform_impl/stdweb/mod.rs +++ b/src/platform_impl/stdweb/mod.rs @@ -1,14 +1,18 @@ use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; -use event::Event; +use event::{Event, StartCause}; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; use icon::Icon; use monitor::{MonitorHandle as RootMH}; use window::{CreationError, MouseCursor, WindowAttributes}; use stdweb::{ - document, - web::html_element::CanvasElement + traits::*, + web::{ + document, + event::*, + html_element::CanvasElement, + } }; -use std::cell::Cell; +use std::cell::{RefCell, RefMut}; use std::collections::VecDeque; use std::collections::vec_deque::IntoIter as VecDequeIter; use std::marker::PhantomData; @@ -59,7 +63,6 @@ impl WindowId { pub struct Window { canvas: CanvasElement, - monitors: VecDeque } impl Window { @@ -86,7 +89,7 @@ impl Window { } pub fn get_position(&self) -> Option { - let bounds = canvas.get_bouding_client_rect(); + let bounds = self.canvas.get_bounding_client_rect(); Some(LogicalPosition { x: bounds.get_x(), y: bounds.get_y(), @@ -105,23 +108,23 @@ impl Window { #[inline] pub fn get_inner_size(&self) -> Option { Some(LogicalSize { - x: self.canvas.width() as f64 - y: self.canvas.height() as f64 + width: self.canvas.width() as f64, + height: self.canvas.height() as f64 }) } #[inline] pub fn get_outer_size(&self) -> Option { Some(LogicalSize { - x: self.canvas.width() as f64 - y: self.canvas.height() as f64 + 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.x as u32); - self.canvas.set_height(size.y as u32); + self.canvas.set_width(size.width as u32); + self.canvas.set_height(size.height as u32); } #[inline] @@ -185,7 +188,7 @@ impl Window { MouseCursor::ColResize => "col-resize", MouseCursor::RowResize => "row-resize", }; - self.canvas.set_attribute("cursor", text); + self.canvas.set_attribute("cursor", text) .expect("Setting the cursor on the canvas"); } @@ -249,7 +252,7 @@ impl Window { #[inline] pub fn get_available_monitors(&self) -> VecDequeIter { - self.monitors.iter() + VecDeque::new().into_iter() } #[inline] @@ -259,25 +262,28 @@ impl Window { #[inline] pub fn id(&self) -> WindowId { - WindowId::dummy() + // TODO ? + unsafe { WindowId::dummy() } } } fn new_rootelw() -> RootELW { RootELW { - p: EventLoopWindowTarget, + p: EventLoopWindowTarget { + _phantom: PhantomData + }, _marker: PhantomData } } -pub struct EventLoop { +pub struct EventLoop { window_target: RootELW, - monitors: VecDeque, - data: Rc>, + data: Rc>>, } -struct EventLoopData { - events: VecDeque, +#[derive(Clone)] +struct EventLoopData { + events: VecDeque>, control: ControlFlow, } @@ -287,7 +293,7 @@ impl EventLoop { } pub fn get_available_monitors(&self) -> VecDequeIter { - self.monitors.iter() + VecDeque::new().into_iter() } pub fn get_primary_monitor(&self) -> MonitorHandle { @@ -299,23 +305,87 @@ impl EventLoop { { // TODO: Create event handlers for the JS events // TODO: how to handle request redraw? + // TODO: onclose (stdweb PR) + // TODO: file dropping, PathBuf isn't useful for web + + let document = &document(); + self.add_event(document, |data, event: BlurEvent| { + }); + self.add_event(document, |data, event: FocusEvent| { + }); + + // TODO: what to do after attaching events unimplemented!(); } pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { - events: self.window_target.p.events.clone() + data: self.data.clone() } } pub fn window_target(&self) -> &RootELW { &self.window_target } + + // Apply all enqueued events + fn apply_events(&mut self, mut event_handler: F, start: StartCause) + where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { + // TODO: how to handle ControlFlow::Exit? + let mut data = self.data.borrow_mut(); + let mut control = data.control.clone(); + let events = &mut data.events; + event_handler(Event::NewEvents(start), &new_rootelw(), &mut control); + for event in events.drain(..) { + event_handler(event, &new_rootelw(), &mut control); + } + event_handler(Event::EventsCleared, &new_rootelw(), &mut control) + } + + fn register_window(&self, other: &Window) { + let canvas = &other.canvas; + + self.add_event(canvas, |data, event: KeyDownEvent| { + // TODO: received character + // TODO: keyboard input + }); + self.add_event(canvas, |data, event: KeyUpEvent| { + // TODO: keyboard input + }); + self.add_event(canvas, |data, _: PointerOutEvent| { + // TODO + }); + self.add_event(canvas, |data, _: PointerOverEvent| { + // TODO + }); + self.add_event(canvas, |data, event: PointerMoveEvent| { + // TODO: mouse move + }); + self.add_event(canvas, |data, event: PointerUpEvent| { + // TODO: mouse pointers + }); + self.add_event(canvas, |data, event: PointerDownEvent| { + // TODO: mouse pointers + }); + } + + fn add_event(&self, target: &impl IEventTarget, mut handler: F) + where E: ConcreteEvent, F: FnMut(RefMut>, E) + 'static { + let data = self.data.clone(); + + target.add_event_listener(move |event: E| { + event.prevent_default(); + event.stop_propagation(); + event.cancel_bubble(); + + handler(data.borrow_mut(), event); + }); + } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone)] pub struct EventLoopProxy { - data: EventLoopData + data: Rc>> } impl EventLoopProxy { From f698d451df95bf6a41a594462020eb8ea4424216 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sat, 2 Mar 2019 12:31:16 -0500 Subject: [PATCH 04/27] Add key and mouse event support that typechecks --- src/lib.rs | 1 + src/platform_impl/stdweb/mod.rs | 315 +++++++++++++++++++++++++++++--- 2 files changed, 293 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4155fa2597..07d908b133 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,6 +112,7 @@ extern crate percent_encoding; #[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] extern crate smithay_client_toolkit as sctk; #[cfg(feature = "stdweb")] +#[macro_use] extern crate stdweb; pub mod dpi; diff --git a/src/platform_impl/stdweb/mod.rs b/src/platform_impl/stdweb/mod.rs index 727011366b..cea168da62 100644 --- a/src/platform_impl/stdweb/mod.rs +++ b/src/platform_impl/stdweb/mod.rs @@ -1,16 +1,18 @@ use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; -use event::{Event, StartCause}; +use event::{DeviceEvent, DeviceId as RootDI, ElementState, Event, KeyboardInput, ModifiersState, MouseButton, ScanCode, StartCause, VirtualKeyCode, WindowEvent}; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; use icon::Icon; use monitor::{MonitorHandle as RootMH}; -use window::{CreationError, MouseCursor, WindowAttributes}; +use window::{CreationError, MouseCursor, WindowId as RootWI, WindowAttributes}; use stdweb::{ + JsSerialize, traits::*, + unstable::TryInto, web::{ document, event::*, html_element::CanvasElement, - } + }, }; use std::cell::{RefCell, RefMut}; use std::collections::VecDeque; @@ -19,15 +21,18 @@ use std::marker::PhantomData; use std::rc::Rc; // TODO: dpi +// TODO: close events (stdweb PR required) // TODO: pointer locking (stdweb PR required) +// TODO: mouse wheel events (stdweb PR required) +// TODO: key event: .which() (stdweb PR) // TODO: should there be a maximization / fullscreen API? #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId; +pub struct DeviceId(i32); impl DeviceId { pub unsafe fn dummy() -> Self { - DeviceId + DeviceId(0) } } @@ -66,7 +71,6 @@ pub struct Window { } impl Window { - // TODO: type of window_target pub fn new(target: &EventLoopWindowTarget, window: WindowAttributes, platform: PlatformSpecificWindowBuilderAttributes) -> Result { unimplemented!(); } @@ -309,9 +313,9 @@ impl EventLoop { // TODO: file dropping, PathBuf isn't useful for web let document = &document(); - self.add_event(document, |data, event: BlurEvent| { + self.add_event(document, |mut data, event: BlurEvent| { }); - self.add_event(document, |data, event: FocusEvent| { + self.add_event(document, |mut data, event: FocusEvent| { }); // TODO: what to do after attaching events @@ -345,27 +349,96 @@ impl EventLoop { fn register_window(&self, other: &Window) { let canvas = &other.canvas; - self.add_event(canvas, |data, event: KeyDownEvent| { - // TODO: received character - // TODO: keyboard input + self.add_event(canvas, |mut data, event: KeyDownEvent| { + let key = event.key(); + let mut characters = key.chars(); + let first = characters.next(); + let second = characters.next(); + if let (Some(key), None) = (first, second) { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::ReceivedCharacter(key) + }); + } + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::KeyboardInput { + // TODO: is there a way to get keyboard device? + device_id: RootDI(unsafe { DeviceId::dummy() }), + input: KeyboardInput { + scancode: scancode(&event), + state: ElementState::Pressed, + virtual_keycode: button_mapping(&event), + modifiers: keyboard_modifiers_state(&event), + } + } + }); }); - self.add_event(canvas, |data, event: KeyUpEvent| { - // TODO: keyboard input + self.add_event(canvas, |mut data, event: KeyUpEvent| { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::KeyboardInput { + // TODO: is there a way to get keyboard device? + device_id: RootDI(unsafe { DeviceId::dummy() }), + input: KeyboardInput { + scancode: scancode(&event), + state: ElementState::Released, + virtual_keycode: button_mapping(&event), + modifiers: keyboard_modifiers_state(&event), + } + } + }); }); - self.add_event(canvas, |data, _: PointerOutEvent| { - // TODO + self.add_event(canvas, |mut data, event: PointerOutEvent| { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorLeft { + device_id: RootDI(DeviceId(event.pointer_id())) + } + }); }); - self.add_event(canvas, |data, _: PointerOverEvent| { - // TODO + self.add_event(canvas, |mut data, event: PointerOverEvent| { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorEntered { + device_id: RootDI(DeviceId(event.pointer_id())) + } + }); }); - self.add_event(canvas, |data, event: PointerMoveEvent| { - // TODO: mouse move + self.add_event(canvas, |mut data, event: PointerMoveEvent| { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorMoved { + device_id: RootDI(DeviceId(event.pointer_id())), + position: LogicalPosition { + x: event.offset_x(), + y: event.offset_y() + }, + modifiers: mouse_modifiers_state(&event) + } + }); }); - self.add_event(canvas, |data, event: PointerUpEvent| { - // TODO: mouse pointers + self.add_event(canvas, |mut data, event: PointerUpEvent| { + data.events.push_back(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) + } + }); }); - self.add_event(canvas, |data, event: PointerDownEvent| { - // TODO: mouse pointers + self.add_event(canvas, |mut data, event: PointerDownEvent| { + data.events.push_back(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) + } + }); }); } @@ -383,6 +456,202 @@ impl EventLoop { } } +fn mouse_modifiers_state(event: &impl IMouseEvent) -> ModifiersState { + ModifiersState { + shift: event.shift_key(), + ctrl: event.ctrl_key(), + alt: event.alt_key(), + logo: event.meta_key(), + } +} + +fn mouse_button(event: &impl IMouseEvent) -> MouseButton { + match event.button() { + stdweb::web::event::MouseButton::Left => MouseButton::Left, + stdweb::web::event::MouseButton::Right => MouseButton::Right, + stdweb::web::event::MouseButton::Wheel => MouseButton::Middle, + stdweb::web::event::MouseButton::Button4 => MouseButton::Other(0), + stdweb::web::event::MouseButton::Button5 => MouseButton::Other(1), + } +} + +fn keyboard_modifiers_state(event: &impl IKeyboardEvent) -> ModifiersState { + ModifiersState { + shift: event.shift_key(), + ctrl: event.ctrl_key(), + alt: event.alt_key(), + logo: event.meta_key(), + } +} + +fn scancode(event: &T) -> ScanCode { + let which = js! ( return @{event}.which(); ); + which.try_into().expect("The which value should be a number") +} + +fn button_mapping(event: &impl IKeyboardEvent) -> 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 + }) +} + #[derive(Clone)] pub struct EventLoopProxy { data: Rc>> From 37d354cf7fdee5a423436899b20e77aab0629b05 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sat, 9 Mar 2019 21:54:29 -0500 Subject: [PATCH 05/27] Get to a state where a canvas is spawned --- examples/window.rs | 6 +- src/event_loop.rs | 2 +- src/platform_impl/stdweb/mod.rs | 119 ++++++++++++++++++-------------- 3 files changed, 75 insertions(+), 52 deletions(-) diff --git a/examples/window.rs b/examples/window.rs index 3d5c7cf7db..b33b5984c1 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,4 +1,7 @@ extern crate winit; +#[macro_use] +extern crate stdweb; + use winit::window::WindowBuilder; use winit::event::{Event, WindowEvent}; use winit::event_loop::{EventLoop, ControlFlow}; @@ -10,9 +13,10 @@ fn main() { .with_title("A fantastic window!") .build(&event_loop) .unwrap(); + console!(log, "Built window!"); event_loop.run(|event, _, control_flow| { - println!("{:?}", event); + console!(log, format!("{:?}", event)); match event { Event::WindowEvent { diff --git a/src/event_loop.rs b/src/event_loop.rs index ca217f7c97..44cc8b90c1 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -139,7 +139,7 @@ impl EventLoop { /// /// [`ControlFlow`]: ./enum.ControlFlow.html #[inline] - pub fn run(self, event_handler: F) -> ! + pub fn run(self, event_handler: F) // TODO: this needs to be ! where F: 'static + FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow) { self.event_loop.run(event_handler) diff --git a/src/platform_impl/stdweb/mod.rs b/src/platform_impl/stdweb/mod.rs index cea168da62..2eda431896 100644 --- a/src/platform_impl/stdweb/mod.rs +++ b/src/platform_impl/stdweb/mod.rs @@ -41,7 +41,8 @@ pub struct MonitorHandle; impl MonitorHandle { pub fn get_hidpi_factor(&self) -> f64 { - unimplemented!(); + // TODO + 1.0 } pub fn get_position(&self) -> PhysicalPosition { @@ -71,8 +72,42 @@ pub struct Window { } impl Window { - pub fn new(target: &EventLoopWindowTarget, window: WindowAttributes, platform: PlatformSpecificWindowBuilderAttributes) -> Result { - unimplemented!(); + pub fn new(target: &EventLoopWindowTarget, attr: WindowAttributes, + _: PlatformSpecificWindowBuilderAttributes) -> Result { + let element = document() + .create_element("canvas") + .map_err(|_| CreationError::OsError("Failed to create canvas element".to_owned()))?; + let canvas: CanvasElement = element.try_into() + .map_err(|_| CreationError::OsError("Failed to create canvas element".to_owned()))?; + document().body() + .ok_or_else(|| CreationError::OsError("Failed to find body node".to_owned()))? + .append_child(&canvas); + let window = Window { canvas }; + if let Some(dimensions) = attr.dimensions { + window.set_inner_size(dimensions); + } else { + window.set_inner_size(LogicalSize { + width: 1024.0, + height: 768.0, + }) + } + // TODO: most of these are no-op, but should they stay here just in case? + window.set_min_dimensions(attr.min_dimensions); + window.set_max_dimensions(attr.max_dimensions); + window.set_resizable(attr.resizable); + window.set_title(&attr.title); + window.set_maximized(attr.maximized); + if attr.visible { + window.show(); + } else { + window.hide(); + } + //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); + target.register_window(&window); + Ok(window) } pub fn set_title(&self, title: &str) { @@ -88,8 +123,7 @@ impl Window { } pub fn request_redraw(&self) { - // TODO: what does this mean - unimplemented!(); + // TODO: what does this mean? If it's a 'present'-style call then it's not necessary } pub fn get_position(&self) -> Option { @@ -106,7 +140,6 @@ impl Window { pub fn set_position(&self, position: LogicalPosition) { // TODO: use CSS? - unimplemented!(); } #[inline] @@ -148,7 +181,8 @@ impl Window { #[inline] pub fn get_hidpi_factor(&self) -> f64 { - unimplemented!(); + // TODO + 1.0 } #[inline] @@ -199,13 +233,13 @@ impl Window { #[inline] pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> { // TODO: pointer capture - unimplemented!(); + Ok(()) } #[inline] pub fn grab_cursor(&self, grab: bool) -> Result<(), String> { // TODO: pointer capture - unimplemented!(); + Ok(()) } #[inline] @@ -217,13 +251,11 @@ impl Window { #[inline] pub fn set_maximized(&self, maximized: bool) { // TODO: should there be a maximization / fullscreen API? - unimplemented!(); } #[inline] pub fn set_fullscreen(&self, monitor: Option) { // TODO: should there be a maximization / fullscreen API? - unimplemented!(); } #[inline] @@ -239,12 +271,11 @@ impl Window { #[inline] pub fn set_window_icon(&self, window_icon: Option) { // TODO: should this set the favicon? - unimplemented!(); } #[inline] pub fn set_ime_spot(&self, position: LogicalPosition) { - unimplemented!(); + // TODO: what is this? } #[inline] @@ -271,18 +302,8 @@ impl Window { } } -fn new_rootelw() -> RootELW { - RootELW { - p: EventLoopWindowTarget { - _phantom: PhantomData - }, - _marker: PhantomData - } -} - pub struct EventLoop { - window_target: RootELW, - data: Rc>>, + elw: RootELW, } #[derive(Clone)] @@ -291,9 +312,23 @@ struct EventLoopData { control: ControlFlow, } +pub struct EventLoopWindowTarget { + data: Rc>>, +} + impl EventLoop { pub fn new() -> Self { - unimplemented!(); + EventLoop { + elw: RootELW { + p: EventLoopWindowTarget { + data: Rc::new(RefCell::new(EventLoopData { + events: VecDeque::new(), + control: ControlFlow::Poll + })) + }, + _marker: PhantomData + } + } } pub fn get_available_monitors(&self) -> VecDequeIter { @@ -304,7 +339,7 @@ impl EventLoop { MonitorHandle } - pub fn run(mut self, event_handler: F) -> ! + pub fn run(mut self, event_handler: F) where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { // TODO: Create event handlers for the JS events @@ -313,39 +348,26 @@ impl EventLoop { // TODO: file dropping, PathBuf isn't useful for web let document = &document(); - self.add_event(document, |mut data, event: BlurEvent| { + self.elw.p.add_event(document, |mut data, event: BlurEvent| { }); - self.add_event(document, |mut data, event: FocusEvent| { + self.elw.p.add_event(document, |mut data, event: FocusEvent| { }); - // TODO: what to do after attaching events - unimplemented!(); + stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? } pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { - data: self.data.clone() + data: self.elw.p.data.clone() } } pub fn window_target(&self) -> &RootELW { - &self.window_target - } - - // Apply all enqueued events - fn apply_events(&mut self, mut event_handler: F, start: StartCause) - where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { - // TODO: how to handle ControlFlow::Exit? - let mut data = self.data.borrow_mut(); - let mut control = data.control.clone(); - let events = &mut data.events; - event_handler(Event::NewEvents(start), &new_rootelw(), &mut control); - for event in events.drain(..) { - event_handler(event, &new_rootelw(), &mut control); - } - event_handler(Event::EventsCleared, &new_rootelw(), &mut control) + &self.elw } +} +impl EventLoopWindowTarget { fn register_window(&self, other: &Window) { let canvas = &other.canvas; @@ -442,6 +464,7 @@ impl EventLoop { }); } + fn add_event(&self, target: &impl IEventTarget, mut handler: F) where E: ConcreteEvent, F: FnMut(RefMut>, E) + 'static { let data = self.data.clone(); @@ -664,10 +687,6 @@ impl EventLoopProxy { } } -pub struct EventLoopWindowTarget { - _phantom: PhantomData -} - #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PlatformSpecificWindowBuilderAttributes; From 283a8dec37f7e251ddd90b7ddff9fa9462877cbd Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sat, 9 Mar 2019 22:23:39 -0500 Subject: [PATCH 06/27] Refactor out the stdweb functionality into different modules --- src/platform_impl/stdweb/events.rs | 222 ++++++++ src/platform_impl/stdweb/input_binds.rs | 202 +++++++ src/platform_impl/stdweb/mod.rs | 693 +----------------------- src/platform_impl/stdweb/window.rs | 285 ++++++++++ 4 files changed, 717 insertions(+), 685 deletions(-) create mode 100644 src/platform_impl/stdweb/events.rs create mode 100644 src/platform_impl/stdweb/input_binds.rs create mode 100644 src/platform_impl/stdweb/window.rs diff --git a/src/platform_impl/stdweb/events.rs b/src/platform_impl/stdweb/events.rs new file mode 100644 index 0000000000..d3b39d9e35 --- /dev/null +++ b/src/platform_impl/stdweb/events.rs @@ -0,0 +1,222 @@ +use super::*; + +use dpi::{LogicalPosition, LogicalSize}; +use event::{DeviceEvent, DeviceId as RootDI, ElementState, Event, KeyboardInput, ModifiersState, MouseButton, ScanCode, StartCause, VirtualKeyCode, WindowEvent}; +use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; +use icon::Icon; +use window::{MouseCursor, WindowId as RootWI}; +use stdweb::{ + JsSerialize, + traits::*, + unstable::TryInto, + web::{ + document, + event::*, + html_element::CanvasElement, + }, +}; +use std::cell::{RefCell, RefMut}; +use std::collections::VecDeque; +use std::collections::vec_deque::IntoIter as VecDequeIter; +use std::marker::PhantomData; +use std::rc::Rc; + +#[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, +} + +#[derive(Clone)] +struct EventLoopData { + events: VecDeque>, + control: ControlFlow, +} + +pub struct EventLoopWindowTarget { + data: Rc>>, +} + +impl EventLoop { + pub fn new() -> Self { + EventLoop { + elw: RootELW { + p: EventLoopWindowTarget { + data: Rc::new(RefCell::new(EventLoopData { + events: VecDeque::new(), + control: ControlFlow::Poll + })) + }, + _marker: PhantomData + } + } + } + + pub fn get_available_monitors(&self) -> VecDequeIter { + VecDeque::new().into_iter() + } + + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle + } + + pub fn run(mut self, event_handler: F) + where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) + { + // TODO: Create event handlers for the JS events + // TODO: how to handle request redraw? + // TODO: onclose (stdweb PR) + // TODO: file dropping, PathBuf isn't useful for web + + let document = &document(); + self.elw.p.add_event(document, |mut data, event: BlurEvent| { + }); + self.elw.p.add_event(document, |mut data, event: FocusEvent| { + }); + + stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? + } + + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { + data: self.elw.p.data.clone() + } + } + + pub fn window_target(&self) -> &RootELW { + &self.elw + } +} + +impl EventLoopWindowTarget { + pub fn register_window(&self, other: &Window) { + let canvas = &other.canvas; + + self.add_event(canvas, |mut data, event: KeyDownEvent| { + let key = event.key(); + let mut characters = key.chars(); + let first = characters.next(); + let second = characters.next(); + if let (Some(key), None) = (first, second) { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::ReceivedCharacter(key) + }); + } + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::KeyboardInput { + // TODO: is there a way to get keyboard device? + device_id: RootDI(unsafe { DeviceId::dummy() }), + input: KeyboardInput { + scancode: scancode(&event), + state: ElementState::Pressed, + virtual_keycode: button_mapping(&event), + modifiers: keyboard_modifiers_state(&event), + } + } + }); + }); + self.add_event(canvas, |mut data, event: KeyUpEvent| { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::KeyboardInput { + // TODO: is there a way to get keyboard device? + device_id: RootDI(unsafe { DeviceId::dummy() }), + input: KeyboardInput { + scancode: scancode(&event), + state: ElementState::Released, + virtual_keycode: button_mapping(&event), + modifiers: keyboard_modifiers_state(&event), + } + } + }); + }); + self.add_event(canvas, |mut data, event: PointerOutEvent| { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorLeft { + device_id: RootDI(DeviceId(event.pointer_id())) + } + }); + }); + self.add_event(canvas, |mut data, event: PointerOverEvent| { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorEntered { + device_id: RootDI(DeviceId(event.pointer_id())) + } + }); + }); + self.add_event(canvas, |mut data, event: PointerMoveEvent| { + data.events.push_back(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorMoved { + device_id: RootDI(DeviceId(event.pointer_id())), + position: LogicalPosition { + x: event.offset_x(), + y: event.offset_y() + }, + modifiers: mouse_modifiers_state(&event) + } + }); + }); + self.add_event(canvas, |mut data, event: PointerUpEvent| { + data.events.push_back(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) + } + }); + }); + self.add_event(canvas, |mut data, event: PointerDownEvent| { + data.events.push_back(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) + } + }); + }); + } + + + fn add_event(&self, target: &impl IEventTarget, mut handler: F) + where E: ConcreteEvent, F: FnMut(RefMut>, E) + 'static { + let data = self.data.clone(); + + target.add_event_listener(move |event: E| { + event.prevent_default(); + event.stop_propagation(); + event.cancel_bubble(); + + handler(data.borrow_mut(), event); + }); + } +} + +#[derive(Clone)] +pub struct EventLoopProxy { + data: Rc>> +} + +impl EventLoopProxy { + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + self.data.borrow_mut().events.push_back(Event::UserEvent(event)); + Ok(()) + } +} + + diff --git a/src/platform_impl/stdweb/input_binds.rs b/src/platform_impl/stdweb/input_binds.rs new file mode 100644 index 0000000000..282c6664e0 --- /dev/null +++ b/src/platform_impl/stdweb/input_binds.rs @@ -0,0 +1,202 @@ +use stdweb::{ + JsSerialize, + web::event::{IKeyboardEvent, IMouseEvent}, + unstable::TryInto +}; +use event::{MouseButton, ModifiersState, ScanCode, VirtualKeyCode}; + +pub fn button_mapping(event: &impl IKeyboardEvent) -> 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: &impl IMouseEvent) -> ModifiersState { + ModifiersState { + shift: event.shift_key(), + ctrl: event.ctrl_key(), + alt: event.alt_key(), + logo: event.meta_key(), + } +} + +pub fn mouse_button(event: &impl IMouseEvent) -> MouseButton { + match event.button() { + stdweb::web::event::MouseButton::Left => MouseButton::Left, + stdweb::web::event::MouseButton::Right => MouseButton::Right, + stdweb::web::event::MouseButton::Wheel => MouseButton::Middle, + stdweb::web::event::MouseButton::Button4 => MouseButton::Other(0), + stdweb::web::event::MouseButton::Button5 => MouseButton::Other(1), + } +} + +pub fn keyboard_modifiers_state(event: &impl IKeyboardEvent) -> ModifiersState { + ModifiersState { + shift: event.shift_key(), + ctrl: event.ctrl_key(), + alt: event.alt_key(), + logo: event.meta_key(), + } +} + +pub fn scancode(event: &T) -> ScanCode { + let which = js! ( return @{event}.which(); ); + which.try_into().expect("The which value should be a number") +} diff --git a/src/platform_impl/stdweb/mod.rs b/src/platform_impl/stdweb/mod.rs index 2eda431896..c2b8733181 100644 --- a/src/platform_impl/stdweb/mod.rs +++ b/src/platform_impl/stdweb/mod.rs @@ -1,24 +1,11 @@ -use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; -use event::{DeviceEvent, DeviceId as RootDI, ElementState, Event, KeyboardInput, ModifiersState, MouseButton, ScanCode, StartCause, VirtualKeyCode, WindowEvent}; -use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; -use icon::Icon; -use monitor::{MonitorHandle as RootMH}; -use window::{CreationError, MouseCursor, WindowId as RootWI, WindowAttributes}; -use stdweb::{ - JsSerialize, - traits::*, - unstable::TryInto, - web::{ - document, - event::*, - html_element::CanvasElement, - }, -}; -use std::cell::{RefCell, RefMut}; -use std::collections::VecDeque; -use std::collections::vec_deque::IntoIter as VecDequeIter; -use std::marker::PhantomData; -use std::rc::Rc; +mod events; +mod input_binds; +mod window; + +pub use self::events::{DeviceId, EventLoop, EventLoopWindowTarget, EventLoopProxy}; +pub use self::window::{MonitorHandle, Window, WindowId, PlatformSpecificWindowBuilderAttributes}; +pub use self::input_binds::{button_mapping, mouse_modifiers_state, mouse_button, keyboard_modifiers_state, scancode}; + // TODO: dpi // TODO: close events (stdweb PR required) @@ -26,667 +13,3 @@ use std::rc::Rc; // TODO: mouse wheel events (stdweb PR required) // TODO: key event: .which() (stdweb PR) // TODO: should there be a maximization / fullscreen API? - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(i32); - -impl DeviceId { - pub unsafe fn dummy() -> Self { - DeviceId(0) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct MonitorHandle; - -impl MonitorHandle { - pub fn get_hidpi_factor(&self) -> f64 { - // TODO - 1.0 - } - - pub fn get_position(&self) -> PhysicalPosition { - unimplemented!(); - } - - pub fn get_dimensions(&self) -> PhysicalSize { - unimplemented!(); - } - - pub fn get_name(&self) -> Option { - unimplemented!(); - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct WindowId; - -impl WindowId { - pub unsafe fn dummy() -> WindowId { - WindowId - } -} - -pub struct Window { - canvas: CanvasElement, -} - -impl Window { - pub fn new(target: &EventLoopWindowTarget, attr: WindowAttributes, - _: PlatformSpecificWindowBuilderAttributes) -> Result { - let element = document() - .create_element("canvas") - .map_err(|_| CreationError::OsError("Failed to create canvas element".to_owned()))?; - let canvas: CanvasElement = element.try_into() - .map_err(|_| CreationError::OsError("Failed to create canvas element".to_owned()))?; - document().body() - .ok_or_else(|| CreationError::OsError("Failed to find body node".to_owned()))? - .append_child(&canvas); - let window = Window { canvas }; - if let Some(dimensions) = attr.dimensions { - window.set_inner_size(dimensions); - } else { - window.set_inner_size(LogicalSize { - width: 1024.0, - height: 768.0, - }) - } - // TODO: most of these are no-op, but should they stay here just in case? - window.set_min_dimensions(attr.min_dimensions); - window.set_max_dimensions(attr.max_dimensions); - window.set_resizable(attr.resizable); - window.set_title(&attr.title); - window.set_maximized(attr.maximized); - if attr.visible { - window.show(); - } else { - window.hide(); - } - //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); - target.register_window(&window); - Ok(window) - } - - pub fn set_title(&self, title: &str) { - document().set_title(title); - } - - pub fn show(&self) { - // Intentionally a no-op - } - - pub fn hide(&self) { - // Intentionally a no-op - } - - pub fn request_redraw(&self) { - // TODO: what does this mean? If it's a 'present'-style call then it's not necessary - } - - pub fn get_position(&self) -> Option { - let bounds = self.canvas.get_bounding_client_rect(); - Some(LogicalPosition { - x: bounds.get_x(), - y: bounds.get_y(), - }) - } - - pub fn get_inner_position(&self) -> Option { - self.get_inner_position() - } - - pub fn set_position(&self, position: LogicalPosition) { - // TODO: use CSS? - } - - #[inline] - pub fn get_inner_size(&self) -> Option { - Some(LogicalSize { - width: self.canvas.width() as f64, - height: self.canvas.height() as f64 - }) - } - - #[inline] - pub fn get_outer_size(&self) -> Option { - Some(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_dimensions(&self, _dimensions: Option) { - // Intentionally a no-op: users can't resize canvas elements - } - - #[inline] - pub fn set_max_dimensions(&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 get_hidpi_factor(&self) -> f64 { - // TODO - 1.0 - } - - #[inline] - pub fn set_cursor(&self, cursor: MouseCursor) { - let text = match cursor { - MouseCursor::Default => "auto", - MouseCursor::Crosshair => "crosshair", - MouseCursor::Hand => "pointer", - MouseCursor::Arrow => "default", - MouseCursor::Move => "move", - MouseCursor::Text => "text", - MouseCursor::Wait => "wait", - MouseCursor::Help => "help", - MouseCursor::Progress => "progress", - - MouseCursor::NotAllowed => "not-allowed", - MouseCursor::ContextMenu => "context-menu", - MouseCursor::Cell => "cell", - MouseCursor::VerticalText => "vertical-text", - MouseCursor::Alias => "alias", - MouseCursor::Copy => "copy", - MouseCursor::NoDrop => "no-drop", - MouseCursor::Grab => "grab", - MouseCursor::Grabbing => "grabbing", - MouseCursor::AllScroll => "all-scroll", - MouseCursor::ZoomIn => "zoom-in", - MouseCursor::ZoomOut => "zoom-out", - - MouseCursor::EResize => "e-resize", - MouseCursor::NResize => "n-resize", - MouseCursor::NeResize => "ne-resize", - MouseCursor::NwResize => "nw-resize", - MouseCursor::SResize => "s-resize", - MouseCursor::SeResize => "se-resize", - MouseCursor::SwResize => "sw-resize", - MouseCursor::WResize => "w-resize", - MouseCursor::EwResize => "ew-resize", - MouseCursor::NsResize => "ns-resize", - MouseCursor::NeswResize => "nesw-resize", - MouseCursor::NwseResize => "nwse-resize", - MouseCursor::ColResize => "col-resize", - MouseCursor::RowResize => "row-resize", - }; - self.canvas.set_attribute("cursor", text) - .expect("Setting the cursor on the canvas"); - } - - #[inline] - pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> { - // TODO: pointer capture - Ok(()) - } - - #[inline] - pub fn grab_cursor(&self, grab: bool) -> Result<(), String> { - // TODO: pointer capture - Ok(()) - } - - #[inline] - pub fn hide_cursor(&self, hide: bool) { - self.canvas.set_attribute("cursor", "none") - .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 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) { - // TODO: should this set the favicon? - } - - #[inline] - pub fn set_ime_spot(&self, position: LogicalPosition) { - // TODO: what is this? - } - - #[inline] - pub fn get_current_monitor(&self) -> RootMH { - RootMH { - inner: MonitorHandle - } - } - - #[inline] - pub fn get_available_monitors(&self) -> VecDequeIter { - VecDeque::new().into_iter() - } - - #[inline] - pub fn get_primary_monitor(&self) -> MonitorHandle { - MonitorHandle - } - - #[inline] - pub fn id(&self) -> WindowId { - // TODO ? - unsafe { WindowId::dummy() } - } -} - -pub struct EventLoop { - elw: RootELW, -} - -#[derive(Clone)] -struct EventLoopData { - events: VecDeque>, - control: ControlFlow, -} - -pub struct EventLoopWindowTarget { - data: Rc>>, -} - -impl EventLoop { - pub fn new() -> Self { - EventLoop { - elw: RootELW { - p: EventLoopWindowTarget { - data: Rc::new(RefCell::new(EventLoopData { - events: VecDeque::new(), - control: ControlFlow::Poll - })) - }, - _marker: PhantomData - } - } - } - - pub fn get_available_monitors(&self) -> VecDequeIter { - VecDeque::new().into_iter() - } - - pub fn get_primary_monitor(&self) -> MonitorHandle { - MonitorHandle - } - - pub fn run(mut self, event_handler: F) - where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) - { - // TODO: Create event handlers for the JS events - // TODO: how to handle request redraw? - // TODO: onclose (stdweb PR) - // TODO: file dropping, PathBuf isn't useful for web - - let document = &document(); - self.elw.p.add_event(document, |mut data, event: BlurEvent| { - }); - self.elw.p.add_event(document, |mut data, event: FocusEvent| { - }); - - stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? - } - - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - data: self.elw.p.data.clone() - } - } - - pub fn window_target(&self) -> &RootELW { - &self.elw - } -} - -impl EventLoopWindowTarget { - fn register_window(&self, other: &Window) { - let canvas = &other.canvas; - - self.add_event(canvas, |mut data, event: KeyDownEvent| { - let key = event.key(); - let mut characters = key.chars(); - let first = characters.next(); - let second = characters.next(); - if let (Some(key), None) = (first, second) { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::ReceivedCharacter(key) - }); - } - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::KeyboardInput { - // TODO: is there a way to get keyboard device? - device_id: RootDI(unsafe { DeviceId::dummy() }), - input: KeyboardInput { - scancode: scancode(&event), - state: ElementState::Pressed, - virtual_keycode: button_mapping(&event), - modifiers: keyboard_modifiers_state(&event), - } - } - }); - }); - self.add_event(canvas, |mut data, event: KeyUpEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::KeyboardInput { - // TODO: is there a way to get keyboard device? - device_id: RootDI(unsafe { DeviceId::dummy() }), - input: KeyboardInput { - scancode: scancode(&event), - state: ElementState::Released, - virtual_keycode: button_mapping(&event), - modifiers: keyboard_modifiers_state(&event), - } - } - }); - }); - self.add_event(canvas, |mut data, event: PointerOutEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorLeft { - device_id: RootDI(DeviceId(event.pointer_id())) - } - }); - }); - self.add_event(canvas, |mut data, event: PointerOverEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorEntered { - device_id: RootDI(DeviceId(event.pointer_id())) - } - }); - }); - self.add_event(canvas, |mut data, event: PointerMoveEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorMoved { - device_id: RootDI(DeviceId(event.pointer_id())), - position: LogicalPosition { - x: event.offset_x(), - y: event.offset_y() - }, - modifiers: mouse_modifiers_state(&event) - } - }); - }); - self.add_event(canvas, |mut data, event: PointerUpEvent| { - data.events.push_back(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) - } - }); - }); - self.add_event(canvas, |mut data, event: PointerDownEvent| { - data.events.push_back(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) - } - }); - }); - } - - - fn add_event(&self, target: &impl IEventTarget, mut handler: F) - where E: ConcreteEvent, F: FnMut(RefMut>, E) + 'static { - let data = self.data.clone(); - - target.add_event_listener(move |event: E| { - event.prevent_default(); - event.stop_propagation(); - event.cancel_bubble(); - - handler(data.borrow_mut(), event); - }); - } -} - -fn mouse_modifiers_state(event: &impl IMouseEvent) -> ModifiersState { - ModifiersState { - shift: event.shift_key(), - ctrl: event.ctrl_key(), - alt: event.alt_key(), - logo: event.meta_key(), - } -} - -fn mouse_button(event: &impl IMouseEvent) -> MouseButton { - match event.button() { - stdweb::web::event::MouseButton::Left => MouseButton::Left, - stdweb::web::event::MouseButton::Right => MouseButton::Right, - stdweb::web::event::MouseButton::Wheel => MouseButton::Middle, - stdweb::web::event::MouseButton::Button4 => MouseButton::Other(0), - stdweb::web::event::MouseButton::Button5 => MouseButton::Other(1), - } -} - -fn keyboard_modifiers_state(event: &impl IKeyboardEvent) -> ModifiersState { - ModifiersState { - shift: event.shift_key(), - ctrl: event.ctrl_key(), - alt: event.alt_key(), - logo: event.meta_key(), - } -} - -fn scancode(event: &T) -> ScanCode { - let which = js! ( return @{event}.which(); ); - which.try_into().expect("The which value should be a number") -} - -fn button_mapping(event: &impl IKeyboardEvent) -> 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 - }) -} - -#[derive(Clone)] -pub struct EventLoopProxy { - data: Rc>> -} - -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.data.borrow_mut().events.push_back(Event::UserEvent(event)); - Ok(()) - } -} - -#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PlatformSpecificWindowBuilderAttributes; - diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs new file mode 100644 index 0000000000..114610410c --- /dev/null +++ b/src/platform_impl/stdweb/window.rs @@ -0,0 +1,285 @@ +use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; +use icon::Icon; +use monitor::{MonitorHandle as RootMH}; +use window::{CreationError, MouseCursor, WindowAttributes}; +use super::EventLoopWindowTarget; +use std::collections::VecDeque; +use std::collections::vec_deque::IntoIter as VecDequeIter; +use stdweb::{ + traits::*, + unstable::TryInto +}; +use stdweb::web::{ + document, + html_element::CanvasElement, +}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct MonitorHandle; + +impl MonitorHandle { + pub fn get_hidpi_factor(&self) -> f64 { + // TODO + 1.0 + } + + pub fn get_position(&self) -> PhysicalPosition { + unimplemented!(); + } + + pub fn get_dimensions(&self) -> PhysicalSize { + unimplemented!(); + } + + pub fn get_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: CanvasElement, +} + +impl Window { + pub fn new(target: &EventLoopWindowTarget, attr: WindowAttributes, + _: PlatformSpecificWindowBuilderAttributes) -> Result { + let element = document() + .create_element("canvas") + .map_err(|_| CreationError::OsError("Failed to create canvas element".to_owned()))?; + let canvas: CanvasElement = element.try_into() + .map_err(|_| CreationError::OsError("Failed to create canvas element".to_owned()))?; + document().body() + .ok_or_else(|| CreationError::OsError("Failed to find body node".to_owned()))? + .append_child(&canvas); + let window = Window { canvas }; + if let Some(dimensions) = attr.dimensions { + window.set_inner_size(dimensions); + } else { + window.set_inner_size(LogicalSize { + width: 1024.0, + height: 768.0, + }) + } + // TODO: most of these are no-op, but should they stay here just in case? + window.set_min_dimensions(attr.min_dimensions); + window.set_max_dimensions(attr.max_dimensions); + window.set_resizable(attr.resizable); + window.set_title(&attr.title); + window.set_maximized(attr.maximized); + if attr.visible { + window.show(); + } else { + window.hide(); + } + //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); + target.register_window(&window); + Ok(window) + } + + pub fn set_title(&self, title: &str) { + document().set_title(title); + } + + pub fn show(&self) { + // Intentionally a no-op + } + + pub fn hide(&self) { + // Intentionally a no-op + } + + pub fn request_redraw(&self) { + // TODO: what does this mean? If it's a 'present'-style call then it's not necessary + } + + pub fn get_position(&self) -> Option { + let bounds = self.canvas.get_bounding_client_rect(); + Some(LogicalPosition { + x: bounds.get_x(), + y: bounds.get_y(), + }) + } + + pub fn get_inner_position(&self) -> Option { + // TODO + None + } + + pub fn set_position(&self, position: LogicalPosition) { + // TODO: use CSS? + } + + #[inline] + pub fn get_inner_size(&self) -> Option { + Some(LogicalSize { + width: self.canvas.width() as f64, + height: self.canvas.height() as f64 + }) + } + + #[inline] + pub fn get_outer_size(&self) -> Option { + Some(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_dimensions(&self, _dimensions: Option) { + // Intentionally a no-op: users can't resize canvas elements + } + + #[inline] + pub fn set_max_dimensions(&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 get_hidpi_factor(&self) -> f64 { + // TODO + 1.0 + } + + #[inline] + pub fn set_cursor(&self, cursor: MouseCursor) { + let text = match cursor { + MouseCursor::Default => "auto", + MouseCursor::Crosshair => "crosshair", + MouseCursor::Hand => "pointer", + MouseCursor::Arrow => "default", + MouseCursor::Move => "move", + MouseCursor::Text => "text", + MouseCursor::Wait => "wait", + MouseCursor::Help => "help", + MouseCursor::Progress => "progress", + + MouseCursor::NotAllowed => "not-allowed", + MouseCursor::ContextMenu => "context-menu", + MouseCursor::Cell => "cell", + MouseCursor::VerticalText => "vertical-text", + MouseCursor::Alias => "alias", + MouseCursor::Copy => "copy", + MouseCursor::NoDrop => "no-drop", + MouseCursor::Grab => "grab", + MouseCursor::Grabbing => "grabbing", + MouseCursor::AllScroll => "all-scroll", + MouseCursor::ZoomIn => "zoom-in", + MouseCursor::ZoomOut => "zoom-out", + + MouseCursor::EResize => "e-resize", + MouseCursor::NResize => "n-resize", + MouseCursor::NeResize => "ne-resize", + MouseCursor::NwResize => "nw-resize", + MouseCursor::SResize => "s-resize", + MouseCursor::SeResize => "se-resize", + MouseCursor::SwResize => "sw-resize", + MouseCursor::WResize => "w-resize", + MouseCursor::EwResize => "ew-resize", + MouseCursor::NsResize => "ns-resize", + MouseCursor::NeswResize => "nesw-resize", + MouseCursor::NwseResize => "nwse-resize", + MouseCursor::ColResize => "col-resize", + MouseCursor::RowResize => "row-resize", + }; + self.canvas.set_attribute("cursor", text) + .expect("Setting the cursor on the canvas"); + } + + #[inline] + pub fn set_cursor_position(&self, position: LogicalPosition) -> Result<(), String> { + // TODO: pointer capture + Ok(()) + } + + #[inline] + pub fn grab_cursor(&self, grab: bool) -> Result<(), String> { + // TODO: pointer capture + Ok(()) + } + + #[inline] + pub fn hide_cursor(&self, hide: bool) { + self.canvas.set_attribute("cursor", "none") + .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 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) { + // TODO: should this set the favicon? + } + + #[inline] + pub fn set_ime_spot(&self, position: LogicalPosition) { + // TODO: what is this? + } + + #[inline] + pub fn get_current_monitor(&self) -> RootMH { + RootMH { + inner: MonitorHandle + } + } + + #[inline] + pub fn get_available_monitors(&self) -> VecDequeIter { + VecDeque::new().into_iter() + } + + #[inline] + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle + } + + #[inline] + pub fn id(&self) -> WindowId { + // TODO ? + unsafe { WindowId::dummy() } + } +} From aaee72422abdab80b016d1c9331b1c3bc5ee6420 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Mon, 11 Mar 2019 22:18:58 -0400 Subject: [PATCH 07/27] Rearchitect to allow API compliance --- src/event_loop.rs | 2 +- src/platform_impl/stdweb/events.rs | 259 ++++++++++++++---------- src/platform_impl/stdweb/input_binds.rs | 2 +- src/platform_impl/stdweb/window.rs | 2 +- 4 files changed, 157 insertions(+), 108 deletions(-) diff --git a/src/event_loop.rs b/src/event_loop.rs index c1a85a5441..ab6839cdc6 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -139,7 +139,7 @@ impl EventLoop { /// /// [`ControlFlow`]: ./enum.ControlFlow.html #[inline] - pub fn run(self, event_handler: F) // TODO: this needs to be ! + pub fn run(self, event_handler: F) -> ! where F: 'static + FnMut(Event, &EventLoopWindowTarget, &mut ControlFlow) { self.event_loop.run(event_handler) diff --git a/src/platform_impl/stdweb/events.rs b/src/platform_impl/stdweb/events.rs index d3b39d9e35..06b8c32b40 100644 --- a/src/platform_impl/stdweb/events.rs +++ b/src/platform_impl/stdweb/events.rs @@ -32,30 +32,55 @@ impl DeviceId { pub struct EventLoop { elw: RootELW, + runner: EventLoopRunnerShared +} + +pub struct EventLoopWindowTarget { + pub(crate) canvases: RefCell>, + _marker: PhantomData +} + +impl EventLoopWindowTarget { + fn new() -> Self { + EventLoopWindowTarget { + canvases: RefCell::new(Vec::new()), + _marker: PhantomData + } + } } #[derive(Clone)] -struct EventLoopData { - events: VecDeque>, - control: ControlFlow, +pub struct EventLoopProxy { + runner: EventLoopRunnerShared } -pub struct EventLoopWindowTarget { - data: Rc>>, +impl EventLoopProxy { + pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { + self.runner.send_event(Event::UserEvent(event)); + Ok(()) + } +} + +type EventLoopRunnerShared = Rc>; + +struct ELRShared { + runner: RefCell>>, + events: RefCell>>, // TODO: this may not be necessary? +} + +struct EventLoopRunner { + control: ControlFlow, + event_handler: Box, &mut ControlFlow)>, } impl EventLoop { pub fn new() -> Self { EventLoop { elw: RootELW { - p: EventLoopWindowTarget { - data: Rc::new(RefCell::new(EventLoopData { - events: VecDeque::new(), - control: ControlFlow::Poll - })) - }, + p: EventLoopWindowTarget::new(), _marker: PhantomData - } + }, + runner: Rc::new(ELRShared::blank()), } } @@ -67,50 +92,41 @@ impl EventLoop { MonitorHandle } - pub fn run(mut self, event_handler: F) + pub fn run(mut self, mut event_handler: F) -> ! where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { // TODO: Create event handlers for the JS events // TODO: how to handle request redraw? // TODO: onclose (stdweb PR) // TODO: file dropping, PathBuf isn't useful for web + let EventLoop { elw, runner } = self; + for canvas in elw.p.canvases.borrow().iter() { + register(&runner, canvas); + } + let relw = RootELW { + p: EventLoopWindowTarget::new(), + _marker: PhantomData + }; + runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl))); let document = &document(); - self.elw.p.add_event(document, |mut data, event: BlurEvent| { - }); - self.elw.p.add_event(document, |mut data, event: FocusEvent| { + add_event(&runner, document, |_, _: BlurEvent| { }); + add_event(&runner, document, |_, _: FocusEvent| { - stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? - } - - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - data: self.elw.p.data.clone() - } - } - - pub fn window_target(&self) -> &RootELW { - &self.elw - } -} - -impl EventLoopWindowTarget { - pub fn register_window(&self, other: &Window) { - let canvas = &other.canvas; - - self.add_event(canvas, |mut data, event: KeyDownEvent| { + }); + add_event(&runner, document, |elrs, event: KeyDownEvent| { let key = event.key(); let mut characters = key.chars(); let first = characters.next(); let second = characters.next(); if let (Some(key), None) = (first, second) { - data.events.push_back(Event::WindowEvent { + elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::ReceivedCharacter(key) }); } - data.events.push_back(Event::WindowEvent { + elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::KeyboardInput { // TODO: is there a way to get keyboard device? @@ -124,8 +140,8 @@ impl EventLoopWindowTarget { } }); }); - self.add_event(canvas, |mut data, event: KeyUpEvent| { - data.events.push_back(Event::WindowEvent { + add_event(&runner, document, |elrs, event: KeyUpEvent| { + elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::KeyboardInput { // TODO: is there a way to get keyboard device? @@ -139,84 +155,117 @@ impl EventLoopWindowTarget { } }); }); - self.add_event(canvas, |mut data, event: PointerOutEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorLeft { - device_id: RootDI(DeviceId(event.pointer_id())) - } - }); + stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? + js! { + throw "Using exceptions for control flow, don't mind me"; + } + unreachable!(); + } + + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { + runner: self.runner.clone() + } + } + + pub fn window_target(&self) -> &RootELW { + &self.elw + } +} + +fn register(elrs: &EventLoopRunnerShared, canvas: &CanvasElement) { + add_event(elrs, canvas, |elrs, event: PointerOutEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorLeft { + device_id: RootDI(DeviceId(event.pointer_id())) + } }); - self.add_event(canvas, |mut data, event: PointerOverEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorEntered { - device_id: RootDI(DeviceId(event.pointer_id())) - } - }); + }); + add_event(elrs, canvas, |elrs, event: PointerOverEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorEntered { + device_id: RootDI(DeviceId(event.pointer_id())) + } }); - self.add_event(canvas, |mut data, event: PointerMoveEvent| { - data.events.push_back(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorMoved { - device_id: RootDI(DeviceId(event.pointer_id())), - position: LogicalPosition { - x: event.offset_x(), - y: event.offset_y() - }, - modifiers: mouse_modifiers_state(&event) - } - }); + }); + add_event(elrs, canvas, |elrs, event: PointerMoveEvent| { + 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(), + y: event.offset_y() + }, + modifiers: mouse_modifiers_state(&event) + } }); - self.add_event(canvas, |mut data, event: PointerUpEvent| { - data.events.push_back(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, |elrs, event: PointerUpEvent| { + 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) + } }); - self.add_event(canvas, |mut data, event: PointerDownEvent| { - data.events.push_back(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, |elrs, event: PointerDownEvent| { + 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) + } }); - } + }); +} +fn add_event(elrs: &EventLoopRunnerShared, target: &impl IEventTarget, mut handler: F) + where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared, E) + 'static { + let elrs = elrs.clone(); // TODO: necessary? + + target.add_event_listener(move |event: E| { + event.prevent_default(); + event.stop_propagation(); + event.cancel_bubble(); - fn add_event(&self, target: &impl IEventTarget, mut handler: F) - where E: ConcreteEvent, F: FnMut(RefMut>, E) + 'static { - let data = self.data.clone(); + handler(&elrs, event); + }); +} - target.add_event_listener(move |event: E| { - event.prevent_default(); - event.stop_propagation(); - event.cancel_bubble(); +impl ELRShared { + fn blank() -> ELRShared { + ELRShared { + runner: RefCell::new(None), + events: RefCell::new(VecDeque::new()) + } + } - handler(data.borrow_mut(), event); + fn set_listener(&self, event_handler: Box, &mut ControlFlow)>) { + *self.runner.borrow_mut() = Some(EventLoopRunner { + control: ControlFlow::Poll, + event_handler }); } -} -#[derive(Clone)] -pub struct EventLoopProxy { - data: Rc>> -} - -impl EventLoopProxy { - pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> { - self.data.borrow_mut().events.push_back(Event::UserEvent(event)); - Ok(()) + // TODO: handle event loop closures + // TODO: handle event buffer + fn send_event(&self, event: Event) { + match *self.runner.borrow_mut() { + Some(ref mut runner) => { + // TODO: bracket this in control flow events? + (runner.event_handler)(event, &mut runner.control); + } + None => () + } } -} +} diff --git a/src/platform_impl/stdweb/input_binds.rs b/src/platform_impl/stdweb/input_binds.rs index 282c6664e0..7068fba0ca 100644 --- a/src/platform_impl/stdweb/input_binds.rs +++ b/src/platform_impl/stdweb/input_binds.rs @@ -197,6 +197,6 @@ pub fn keyboard_modifiers_state(event: &impl IKeyboardEvent) -> ModifiersState { } pub fn scancode(event: &T) -> ScanCode { - let which = js! ( return @{event}.which(); ); + let which = js! ( return @{event}.which; ); which.try_into().expect("The which value should be a number") } diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index 114610410c..fd6d6f57d1 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -63,6 +63,7 @@ impl Window { document().body() .ok_or_else(|| CreationError::OsError("Failed to find body node".to_owned()))? .append_child(&canvas); + target.canvases.borrow_mut().push(canvas.clone()); let window = Window { canvas }; if let Some(dimensions) = attr.dimensions { window.set_inner_size(dimensions); @@ -87,7 +88,6 @@ impl Window { window.set_decorations(attr.decorations); window.set_always_on_top(attr.always_on_top); window.set_window_icon(attr.window_icon); - target.register_window(&window); Ok(window) } From d1deba8620127608beeb80d8e8ee4ec7e3e5e733 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Mon, 11 Mar 2019 22:22:21 -0400 Subject: [PATCH 08/27] Rename modules --- src/platform_impl/stdweb/event_loop.rs | 271 +++++++++++++++ src/platform_impl/stdweb/events.rs | 443 ++++++++++-------------- src/platform_impl/stdweb/input_binds.rs | 202 ----------- src/platform_impl/stdweb/mod.rs | 6 +- 4 files changed, 461 insertions(+), 461 deletions(-) create mode 100644 src/platform_impl/stdweb/event_loop.rs delete mode 100644 src/platform_impl/stdweb/input_binds.rs diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs new file mode 100644 index 0000000000..06b8c32b40 --- /dev/null +++ b/src/platform_impl/stdweb/event_loop.rs @@ -0,0 +1,271 @@ +use super::*; + +use dpi::{LogicalPosition, LogicalSize}; +use event::{DeviceEvent, DeviceId as RootDI, ElementState, Event, KeyboardInput, ModifiersState, MouseButton, ScanCode, StartCause, VirtualKeyCode, WindowEvent}; +use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; +use icon::Icon; +use window::{MouseCursor, WindowId as RootWI}; +use stdweb::{ + JsSerialize, + traits::*, + unstable::TryInto, + web::{ + document, + event::*, + html_element::CanvasElement, + }, +}; +use std::cell::{RefCell, RefMut}; +use std::collections::VecDeque; +use std::collections::vec_deque::IntoIter as VecDequeIter; +use std::marker::PhantomData; +use std::rc::Rc; + +#[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, + runner: EventLoopRunnerShared +} + +pub struct EventLoopWindowTarget { + pub(crate) canvases: RefCell>, + _marker: PhantomData +} + +impl EventLoopWindowTarget { + fn new() -> Self { + EventLoopWindowTarget { + canvases: RefCell::new(Vec::new()), + _marker: PhantomData + } + } +} + +#[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(()) + } +} + +type EventLoopRunnerShared = Rc>; + +struct ELRShared { + runner: RefCell>>, + events: RefCell>>, // TODO: this may not be necessary? +} + +struct EventLoopRunner { + control: ControlFlow, + event_handler: Box, &mut ControlFlow)>, +} + +impl EventLoop { + pub fn new() -> Self { + EventLoop { + elw: RootELW { + p: EventLoopWindowTarget::new(), + _marker: PhantomData + }, + runner: Rc::new(ELRShared::blank()), + } + } + + pub fn get_available_monitors(&self) -> VecDequeIter { + VecDeque::new().into_iter() + } + + pub fn get_primary_monitor(&self) -> MonitorHandle { + MonitorHandle + } + + pub fn run(mut self, mut event_handler: F) -> ! + where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) + { + // TODO: Create event handlers for the JS events + // TODO: how to handle request redraw? + // TODO: onclose (stdweb PR) + // TODO: file dropping, PathBuf isn't useful for web + let EventLoop { elw, runner } = self; + for canvas in elw.p.canvases.borrow().iter() { + register(&runner, canvas); + } + 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, |_, _: BlurEvent| { + }); + add_event(&runner, document, |_, _: FocusEvent| { + + }); + add_event(&runner, document, |elrs, event: KeyDownEvent| { + 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 { + // TODO: is there a way to get keyboard device? + 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, |elrs, event: KeyUpEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::KeyboardInput { + // TODO: is there a way to get keyboard device? + device_id: RootDI(unsafe { DeviceId::dummy() }), + input: KeyboardInput { + scancode: scancode(&event), + state: ElementState::Released, + virtual_keycode: button_mapping(&event), + modifiers: keyboard_modifiers_state(&event), + } + } + }); + }); + stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? + js! { + throw "Using exceptions for control flow, don't mind me"; + } + unreachable!(); + } + + pub fn create_proxy(&self) -> EventLoopProxy { + EventLoopProxy { + runner: self.runner.clone() + } + } + + pub fn window_target(&self) -> &RootELW { + &self.elw + } +} + +fn register(elrs: &EventLoopRunnerShared, canvas: &CanvasElement) { + add_event(elrs, canvas, |elrs, event: PointerOutEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorLeft { + device_id: RootDI(DeviceId(event.pointer_id())) + } + }); + }); + add_event(elrs, canvas, |elrs, event: PointerOverEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::CursorEntered { + device_id: RootDI(DeviceId(event.pointer_id())) + } + }); + }); + add_event(elrs, canvas, |elrs, event: PointerMoveEvent| { + 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(), + y: event.offset_y() + }, + modifiers: mouse_modifiers_state(&event) + } + }); + }); + add_event(elrs, canvas, |elrs, event: PointerUpEvent| { + 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, |elrs, event: PointerDownEvent| { + 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) + } + }); + }); +} + +fn add_event(elrs: &EventLoopRunnerShared, target: &impl IEventTarget, mut handler: F) + where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared, E) + 'static { + let elrs = elrs.clone(); // TODO: necessary? + + target.add_event_listener(move |event: E| { + event.prevent_default(); + event.stop_propagation(); + event.cancel_bubble(); + + handler(&elrs, event); + }); +} + +impl ELRShared { + fn blank() -> ELRShared { + ELRShared { + runner: RefCell::new(None), + events: RefCell::new(VecDeque::new()) + } + } + + fn set_listener(&self, event_handler: Box, &mut ControlFlow)>) { + *self.runner.borrow_mut() = Some(EventLoopRunner { + control: ControlFlow::Poll, + event_handler + }); + } + + // TODO: handle event loop closures + // TODO: handle event buffer + fn send_event(&self, event: Event) { + match *self.runner.borrow_mut() { + Some(ref mut runner) => { + // TODO: bracket this in control flow events? + (runner.event_handler)(event, &mut runner.control); + } + None => () + } + } + +} + diff --git a/src/platform_impl/stdweb/events.rs b/src/platform_impl/stdweb/events.rs index 06b8c32b40..7068fba0ca 100644 --- a/src/platform_impl/stdweb/events.rs +++ b/src/platform_impl/stdweb/events.rs @@ -1,271 +1,202 @@ -use super::*; - -use dpi::{LogicalPosition, LogicalSize}; -use event::{DeviceEvent, DeviceId as RootDI, ElementState, Event, KeyboardInput, ModifiersState, MouseButton, ScanCode, StartCause, VirtualKeyCode, WindowEvent}; -use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; -use icon::Icon; -use window::{MouseCursor, WindowId as RootWI}; use stdweb::{ JsSerialize, - traits::*, - unstable::TryInto, - web::{ - document, - event::*, - html_element::CanvasElement, - }, + web::event::{IKeyboardEvent, IMouseEvent}, + unstable::TryInto }; -use std::cell::{RefCell, RefMut}; -use std::collections::VecDeque; -use std::collections::vec_deque::IntoIter as VecDequeIter; -use std::marker::PhantomData; -use std::rc::Rc; - -#[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, - runner: EventLoopRunnerShared -} - -pub struct EventLoopWindowTarget { - pub(crate) canvases: RefCell>, - _marker: PhantomData +use event::{MouseButton, ModifiersState, ScanCode, VirtualKeyCode}; + +pub fn button_mapping(event: &impl IKeyboardEvent) -> 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 + }) } -impl EventLoopWindowTarget { - fn new() -> Self { - EventLoopWindowTarget { - canvases: RefCell::new(Vec::new()), - _marker: PhantomData - } +pub fn mouse_modifiers_state(event: &impl IMouseEvent) -> ModifiersState { + ModifiersState { + shift: event.shift_key(), + ctrl: event.ctrl_key(), + alt: event.alt_key(), + logo: event.meta_key(), } } -#[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 fn mouse_button(event: &impl IMouseEvent) -> MouseButton { + match event.button() { + stdweb::web::event::MouseButton::Left => MouseButton::Left, + stdweb::web::event::MouseButton::Right => MouseButton::Right, + stdweb::web::event::MouseButton::Wheel => MouseButton::Middle, + stdweb::web::event::MouseButton::Button4 => MouseButton::Other(0), + stdweb::web::event::MouseButton::Button5 => MouseButton::Other(1), } } -type EventLoopRunnerShared = Rc>; - -struct ELRShared { - runner: RefCell>>, - events: RefCell>>, // TODO: this may not be necessary? -} - -struct EventLoopRunner { - control: ControlFlow, - event_handler: Box, &mut ControlFlow)>, -} - -impl EventLoop { - pub fn new() -> Self { - EventLoop { - elw: RootELW { - p: EventLoopWindowTarget::new(), - _marker: PhantomData - }, - runner: Rc::new(ELRShared::blank()), - } - } - - pub fn get_available_monitors(&self) -> VecDequeIter { - VecDeque::new().into_iter() - } - - pub fn get_primary_monitor(&self) -> MonitorHandle { - MonitorHandle - } - - pub fn run(mut self, mut event_handler: F) -> ! - where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) - { - // TODO: Create event handlers for the JS events - // TODO: how to handle request redraw? - // TODO: onclose (stdweb PR) - // TODO: file dropping, PathBuf isn't useful for web - let EventLoop { elw, runner } = self; - for canvas in elw.p.canvases.borrow().iter() { - register(&runner, canvas); - } - 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, |_, _: BlurEvent| { - }); - add_event(&runner, document, |_, _: FocusEvent| { - - }); - add_event(&runner, document, |elrs, event: KeyDownEvent| { - 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 { - // TODO: is there a way to get keyboard device? - 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, |elrs, event: KeyUpEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::KeyboardInput { - // TODO: is there a way to get keyboard device? - device_id: RootDI(unsafe { DeviceId::dummy() }), - input: KeyboardInput { - scancode: scancode(&event), - state: ElementState::Released, - virtual_keycode: button_mapping(&event), - modifiers: keyboard_modifiers_state(&event), - } - } - }); - }); - stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? - js! { - throw "Using exceptions for control flow, don't mind me"; - } - unreachable!(); - } - - pub fn create_proxy(&self) -> EventLoopProxy { - EventLoopProxy { - runner: self.runner.clone() - } - } - - pub fn window_target(&self) -> &RootELW { - &self.elw +pub fn keyboard_modifiers_state(event: &impl IKeyboardEvent) -> ModifiersState { + ModifiersState { + shift: event.shift_key(), + ctrl: event.ctrl_key(), + alt: event.alt_key(), + logo: event.meta_key(), } } -fn register(elrs: &EventLoopRunnerShared, canvas: &CanvasElement) { - add_event(elrs, canvas, |elrs, event: PointerOutEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorLeft { - device_id: RootDI(DeviceId(event.pointer_id())) - } - }); - }); - add_event(elrs, canvas, |elrs, event: PointerOverEvent| { - elrs.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::CursorEntered { - device_id: RootDI(DeviceId(event.pointer_id())) - } - }); - }); - add_event(elrs, canvas, |elrs, event: PointerMoveEvent| { - 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(), - y: event.offset_y() - }, - modifiers: mouse_modifiers_state(&event) - } - }); - }); - add_event(elrs, canvas, |elrs, event: PointerUpEvent| { - 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, |elrs, event: PointerDownEvent| { - 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) - } - }); - }); -} - -fn add_event(elrs: &EventLoopRunnerShared, target: &impl IEventTarget, mut handler: F) - where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared, E) + 'static { - let elrs = elrs.clone(); // TODO: necessary? - - target.add_event_listener(move |event: E| { - event.prevent_default(); - event.stop_propagation(); - event.cancel_bubble(); - - handler(&elrs, event); - }); +pub fn scancode(event: &T) -> ScanCode { + let which = js! ( return @{event}.which; ); + which.try_into().expect("The which value should be a number") } - -impl ELRShared { - fn blank() -> ELRShared { - ELRShared { - runner: RefCell::new(None), - events: RefCell::new(VecDeque::new()) - } - } - - fn set_listener(&self, event_handler: Box, &mut ControlFlow)>) { - *self.runner.borrow_mut() = Some(EventLoopRunner { - control: ControlFlow::Poll, - event_handler - }); - } - - // TODO: handle event loop closures - // TODO: handle event buffer - fn send_event(&self, event: Event) { - match *self.runner.borrow_mut() { - Some(ref mut runner) => { - // TODO: bracket this in control flow events? - (runner.event_handler)(event, &mut runner.control); - } - None => () - } - } - -} - diff --git a/src/platform_impl/stdweb/input_binds.rs b/src/platform_impl/stdweb/input_binds.rs deleted file mode 100644 index 7068fba0ca..0000000000 --- a/src/platform_impl/stdweb/input_binds.rs +++ /dev/null @@ -1,202 +0,0 @@ -use stdweb::{ - JsSerialize, - web::event::{IKeyboardEvent, IMouseEvent}, - unstable::TryInto -}; -use event::{MouseButton, ModifiersState, ScanCode, VirtualKeyCode}; - -pub fn button_mapping(event: &impl IKeyboardEvent) -> 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: &impl IMouseEvent) -> ModifiersState { - ModifiersState { - shift: event.shift_key(), - ctrl: event.ctrl_key(), - alt: event.alt_key(), - logo: event.meta_key(), - } -} - -pub fn mouse_button(event: &impl IMouseEvent) -> MouseButton { - match event.button() { - stdweb::web::event::MouseButton::Left => MouseButton::Left, - stdweb::web::event::MouseButton::Right => MouseButton::Right, - stdweb::web::event::MouseButton::Wheel => MouseButton::Middle, - stdweb::web::event::MouseButton::Button4 => MouseButton::Other(0), - stdweb::web::event::MouseButton::Button5 => MouseButton::Other(1), - } -} - -pub fn keyboard_modifiers_state(event: &impl IKeyboardEvent) -> ModifiersState { - ModifiersState { - shift: event.shift_key(), - ctrl: event.ctrl_key(), - alt: event.alt_key(), - logo: event.meta_key(), - } -} - -pub fn scancode(event: &T) -> ScanCode { - let which = js! ( return @{event}.which; ); - which.try_into().expect("The which value should be a number") -} diff --git a/src/platform_impl/stdweb/mod.rs b/src/platform_impl/stdweb/mod.rs index c2b8733181..9a2aab6be4 100644 --- a/src/platform_impl/stdweb/mod.rs +++ b/src/platform_impl/stdweb/mod.rs @@ -1,10 +1,10 @@ +mod event_loop; mod events; -mod input_binds; mod window; -pub use self::events::{DeviceId, EventLoop, EventLoopWindowTarget, EventLoopProxy}; +pub use self::event_loop::{DeviceId, EventLoop, EventLoopWindowTarget, EventLoopProxy}; pub use self::window::{MonitorHandle, Window, WindowId, PlatformSpecificWindowBuilderAttributes}; -pub use self::input_binds::{button_mapping, mouse_modifiers_state, mouse_button, keyboard_modifiers_state, scancode}; +pub use self::events::{button_mapping, mouse_modifiers_state, mouse_button, keyboard_modifiers_state, scancode}; // TODO: dpi From a5166baba21c67a0898db6b7c538f3a0405c324b Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sat, 16 Mar 2019 18:40:35 -0400 Subject: [PATCH 09/27] Implement request_redraw --- src/platform_impl/stdweb/event_loop.rs | 33 +++++++++++++------------- src/platform_impl/stdweb/mod.rs | 2 +- src/platform_impl/stdweb/window.rs | 25 +++++++++++++++---- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 06b8c32b40..478aa572f3 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -32,19 +32,19 @@ impl DeviceId { pub struct EventLoop { elw: RootELW, - runner: EventLoopRunnerShared } pub struct EventLoopWindowTarget { - pub(crate) canvases: RefCell>, - _marker: PhantomData + pub(crate) runner: EventLoopRunnerShared, } impl EventLoopWindowTarget { fn new() -> Self { EventLoopWindowTarget { - canvases: RefCell::new(Vec::new()), - _marker: PhantomData + runner: Rc::new(ELRShared { + runner: RefCell::new(None), + events: RefCell::new(VecDeque::new()) + }) } } } @@ -61,9 +61,9 @@ impl EventLoopProxy { } } -type EventLoopRunnerShared = Rc>; +pub type EventLoopRunnerShared = Rc>; -struct ELRShared { +pub struct ELRShared { runner: RefCell>>, events: RefCell>>, // TODO: this may not be necessary? } @@ -80,7 +80,6 @@ impl EventLoop { p: EventLoopWindowTarget::new(), _marker: PhantomData }, - runner: Rc::new(ELRShared::blank()), } } @@ -95,14 +94,11 @@ impl EventLoop { pub fn run(mut self, mut event_handler: F) -> ! where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { - // TODO: Create event handlers for the JS events // TODO: how to handle request redraw? // TODO: onclose (stdweb PR) // TODO: file dropping, PathBuf isn't useful for web - let EventLoop { elw, runner } = self; - for canvas in elw.p.canvases.borrow().iter() { - register(&runner, canvas); - } + let runner = self.elw.p.runner; + let relw = RootELW { p: EventLoopWindowTarget::new(), _marker: PhantomData @@ -156,6 +152,9 @@ impl EventLoop { }); }); stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? + + // 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 '!' js! { throw "Using exceptions for control flow, don't mind me"; } @@ -164,7 +163,7 @@ impl EventLoop { pub fn create_proxy(&self) -> EventLoopProxy { EventLoopProxy { - runner: self.runner.clone() + runner: self.elw.p.runner.clone() } } @@ -173,7 +172,7 @@ impl EventLoop { } } -fn register(elrs: &EventLoopRunnerShared, canvas: &CanvasElement) { +pub fn register(elrs: &EventLoopRunnerShared, canvas: &CanvasElement) { add_event(elrs, canvas, |elrs, event: PointerOutEvent| { elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), @@ -229,7 +228,7 @@ fn register(elrs: &EventLoopRunnerShared, canvas: &CanvasElement) fn add_event(elrs: &EventLoopRunnerShared, target: &impl IEventTarget, mut handler: F) where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared, E) + 'static { - let elrs = elrs.clone(); // TODO: necessary? + let elrs = elrs.clone(); target.add_event_listener(move |event: E| { event.prevent_default(); @@ -257,7 +256,7 @@ impl ELRShared { // TODO: handle event loop closures // TODO: handle event buffer - fn send_event(&self, event: Event) { + pub fn send_event(&self, event: Event) { match *self.runner.borrow_mut() { Some(ref mut runner) => { // TODO: bracket this in control flow events? diff --git a/src/platform_impl/stdweb/mod.rs b/src/platform_impl/stdweb/mod.rs index 9a2aab6be4..82d8a1895f 100644 --- a/src/platform_impl/stdweb/mod.rs +++ b/src/platform_impl/stdweb/mod.rs @@ -2,7 +2,7 @@ mod event_loop; mod events; mod window; -pub use self::event_loop::{DeviceId, EventLoop, EventLoopWindowTarget, EventLoopProxy}; +pub use self::event_loop::{DeviceId, EventLoop, EventLoopRunnerShared, EventLoopWindowTarget, EventLoopProxy, register}; pub use self::window::{MonitorHandle, Window, WindowId, PlatformSpecificWindowBuilderAttributes}; pub use self::events::{button_mapping, mouse_modifiers_state, mouse_button, keyboard_modifiers_state, scancode}; diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index fd6d6f57d1..a66bc19113 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -1,8 +1,9 @@ use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; +use event::{Event, WindowEvent}; use icon::Icon; use monitor::{MonitorHandle as RootMH}; -use window::{CreationError, MouseCursor, WindowAttributes}; -use super::EventLoopWindowTarget; +use window::{CreationError, MouseCursor, WindowAttributes, WindowId as RootWI}; +use super::{EventLoopWindowTarget, EventLoopRunnerShared, register}; use std::collections::VecDeque; use std::collections::vec_deque::IntoIter as VecDequeIter; use stdweb::{ @@ -50,6 +51,7 @@ impl WindowId { pub struct Window { pub(crate) canvas: CanvasElement, + pub(crate) redraw: Box } impl Window { @@ -63,8 +65,20 @@ impl Window { document().body() .ok_or_else(|| CreationError::OsError("Failed to find body node".to_owned()))? .append_child(&canvas); - target.canvases.borrow_mut().push(canvas.clone()); - let window = Window { canvas }; + + register(&target.runner, &canvas); + + let runner = target.runner.clone(); + let redraw = Box::new(move || runner.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::RedrawRequested + })); + + let window = Window { + canvas, + redraw + }; + if let Some(dimensions) = attr.dimensions { window.set_inner_size(dimensions); } else { @@ -88,6 +102,7 @@ impl Window { window.set_decorations(attr.decorations); window.set_always_on_top(attr.always_on_top); window.set_window_icon(attr.window_icon); + Ok(window) } @@ -104,7 +119,7 @@ impl Window { } pub fn request_redraw(&self) { - // TODO: what does this mean? If it's a 'present'-style call then it's not necessary + (self.redraw)(); } pub fn get_position(&self) -> Option { From 96786bbb87eae65337b4cbe4cf6a2c85af089b8d Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sat, 16 Mar 2019 18:44:13 -0400 Subject: [PATCH 10/27] Implement focus event --- src/platform_impl/stdweb/event_loop.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 478aa572f3..68a5efeee5 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -106,9 +106,17 @@ impl EventLoop { runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl))); let document = &document(); - add_event(&runner, document, |_, _: BlurEvent| { + add_event(&runner, document, |elrs, _: BlurEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::Focused(false) + }); }); - add_event(&runner, document, |_, _: FocusEvent| { + add_event(&runner, document, |elrs, _: FocusEvent| { + elrs.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::Focused(true) + }); }); add_event(&runner, document, |elrs, event: KeyDownEvent| { From 85446d81f3f3d4bde288f8ad55b6ccf43a198b06 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sat, 16 Mar 2019 18:51:11 -0400 Subject: [PATCH 11/27] Fix warnings --- src/platform_impl/stdweb/event_loop.rs | 20 ++++------------ src/platform_impl/stdweb/window.rs | 33 ++++++++++++++++---------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 68a5efeee5..ea2b9acd35 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -1,21 +1,18 @@ use super::*; -use dpi::{LogicalPosition, LogicalSize}; -use event::{DeviceEvent, DeviceId as RootDI, ElementState, Event, KeyboardInput, ModifiersState, MouseButton, ScanCode, StartCause, VirtualKeyCode, WindowEvent}; +use dpi::LogicalPosition; +use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, StartCause, WindowEvent}; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; -use icon::Icon; -use window::{MouseCursor, WindowId as RootWI}; +use window::{WindowId as RootWI}; use stdweb::{ - JsSerialize, traits::*, - unstable::TryInto, web::{ document, event::*, html_element::CanvasElement, }, }; -use std::cell::{RefCell, RefMut}; +use std::cell::RefCell; use std::collections::VecDeque; use std::collections::vec_deque::IntoIter as VecDequeIter; use std::marker::PhantomData; @@ -91,7 +88,7 @@ impl EventLoop { MonitorHandle } - pub fn run(mut self, mut event_handler: F) -> ! + pub fn run(self, mut event_handler: F) -> ! where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { // TODO: how to handle request redraw? @@ -248,13 +245,6 @@ fn add_event(elrs: &EventLoopRunnerShared, target: &impl IE } impl ELRShared { - fn blank() -> ELRShared { - ELRShared { - runner: RefCell::new(None), - events: RefCell::new(VecDeque::new()) - } - } - fn set_listener(&self, event_handler: Box, &mut ControlFlow)>) { *self.runner.borrow_mut() = Some(EventLoopRunner { control: ControlFlow::Poll, diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index a66bc19113..df875f03ed 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -3,9 +3,10 @@ use event::{Event, WindowEvent}; use icon::Icon; use monitor::{MonitorHandle as RootMH}; use window::{CreationError, MouseCursor, WindowAttributes, WindowId as RootWI}; -use super::{EventLoopWindowTarget, EventLoopRunnerShared, register}; +use super::{EventLoopWindowTarget, register}; use std::collections::VecDeque; use std::collections::vec_deque::IntoIter as VecDequeIter; +use std::cell::RefCell; use stdweb::{ traits::*, unstable::TryInto @@ -51,7 +52,8 @@ impl WindowId { pub struct Window { pub(crate) canvas: CanvasElement, - pub(crate) redraw: Box + pub(crate) redraw: Box, + previous_pointer: RefCell<&'static str> } impl Window { @@ -76,7 +78,8 @@ impl Window { let window = Window { canvas, - redraw + redraw, + previous_pointer: RefCell::new("auto") }; if let Some(dimensions) = attr.dimensions { @@ -135,7 +138,7 @@ impl Window { None } - pub fn set_position(&self, position: LogicalPosition) { + pub fn set_position(&self, _position: LogicalPosition) { // TODO: use CSS? } @@ -223,35 +226,41 @@ impl Window { MouseCursor::ColResize => "col-resize", MouseCursor::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<(), String> { + pub fn set_cursor_position(&self, _position: LogicalPosition) -> Result<(), String> { // TODO: pointer capture Ok(()) } #[inline] - pub fn grab_cursor(&self, grab: bool) -> Result<(), String> { + pub fn grab_cursor(&self, _grab: bool) -> Result<(), String> { // TODO: pointer capture Ok(()) } #[inline] pub fn hide_cursor(&self, hide: bool) { - self.canvas.set_attribute("cursor", "none") - .expect("Setting the cursor on the canvas"); + if hide { + 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) { + pub fn set_maximized(&self, _maximized: bool) { // TODO: should there be a maximization / fullscreen API? } #[inline] - pub fn set_fullscreen(&self, monitor: Option) { + pub fn set_fullscreen(&self, _monitor: Option) { // TODO: should there be a maximization / fullscreen API? } @@ -266,12 +275,12 @@ impl Window { } #[inline] - pub fn set_window_icon(&self, window_icon: Option) { + pub fn set_window_icon(&self, _window_icon: Option) { // TODO: should this set the favicon? } #[inline] - pub fn set_ime_spot(&self, position: LogicalPosition) { + pub fn set_ime_spot(&self, _position: LogicalPosition) { // TODO: what is this? } From b09629f1d4b0e09ac5215e5a861a8ee72e10429f Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Mon, 18 Mar 2019 22:13:30 -0400 Subject: [PATCH 12/27] Handle ControlFlow::Exit --- src/platform_impl/stdweb/event_loop.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index ea2b9acd35..0602c1972c 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -67,6 +67,7 @@ pub struct ELRShared { struct EventLoopRunner { control: ControlFlow, + handling: bool, event_handler: Box, &mut ControlFlow)>, } @@ -89,13 +90,9 @@ impl EventLoop { } pub fn run(self, mut event_handler: F) -> ! - where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) - { - // TODO: how to handle request redraw? - // TODO: onclose (stdweb PR) - // TODO: file dropping, PathBuf isn't useful for web + where F: 'static + FnMut(Event, &RootELW, &mut ControlFlow) { let runner = self.elw.p.runner; - + let relw = RootELW { p: EventLoopWindowTarget::new(), _marker: PhantomData @@ -130,7 +127,6 @@ impl EventLoop { elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::KeyboardInput { - // TODO: is there a way to get keyboard device? device_id: RootDI(unsafe { DeviceId::dummy() }), input: KeyboardInput { scancode: scancode(&event), @@ -145,7 +141,6 @@ impl EventLoop { elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::KeyboardInput { - // TODO: is there a way to get keyboard device? device_id: RootDI(unsafe { DeviceId::dummy() }), input: KeyboardInput { scancode: scancode(&event), @@ -248,6 +243,7 @@ impl ELRShared { fn set_listener(&self, event_handler: Box, &mut ControlFlow)>) { *self.runner.borrow_mut() = Some(EventLoopRunner { control: ControlFlow::Poll, + handling: false event_handler }); } @@ -256,11 +252,17 @@ impl ELRShared { // TODO: handle event buffer pub fn send_event(&self, event: Event) { match *self.runner.borrow_mut() { - Some(ref mut runner) => { + Some(ref mut runner) if !runner.handling => { + runner.handling = true; + let closed = runner.control == ControlFlow::Exit; // TODO: bracket this in control flow events? (runner.event_handler)(event, &mut runner.control); + if closed { + runner.control = ControlFlow::Exit; + } + runner.handling = false; } - None => () + _ => self.events.borrow_mut().push_back(event) } } From 7c6bdcc459bf33592f7c996c4d2506ea89ad0f6b Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Fri, 22 Mar 2019 22:15:49 -0400 Subject: [PATCH 13/27] Handle ControlFlow::Exit and dealing with events-in-events --- src/platform_impl/stdweb/event_loop.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 0602c1972c..2827138c60 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -231,6 +231,11 @@ fn add_event(elrs: &EventLoopRunnerShared, target: &impl IE let elrs = elrs.clone(); target.add_event_listener(move |event: E| { + // Don't capture the event if the events loop has been destroyed + if elrs.runner.borrow().control == ControlFlow::Exit { + return; + } + event.prevent_default(); event.stop_propagation(); event.cancel_bubble(); @@ -251,6 +256,22 @@ impl ELRShared { // TODO: handle event loop closures // TODO: handle event buffer pub fn send_event(&self, event: Event) { + let start_cause = StartCause::Poll; // TODO: this is obviously not right + self.handle_start(StartCause::Poll); + self.handle_event(event); + self.handle_event(Event::EventsCleared); + } + + fn handle_start(&self, start: StartCause) { + let is_handling = if Some(ref runner) = *self.runner.borrow() { + runner.handling + } else { + false + }; + self.handle_event(Event::StartCause(is_handling)); + } + + fn handle_event(&self, event: Event) { match *self.runner.borrow_mut() { Some(ref mut runner) if !runner.handling => { runner.handling = true; @@ -264,6 +285,11 @@ impl ELRShared { } _ => self.events.borrow_mut().push_back(event) } + if self.runner.borrow().is_some() { + if let Some(event) = self.events.borrow_mut().pop_front() { + self.handle_event(event); + } + } } } From 9e25561edf868dd077926f2b108407b917560c5c Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Tue, 2 Apr 2019 22:31:30 -0400 Subject: [PATCH 14/27] Fix compile failures and add canvas positioning --- src/platform_impl/stdweb/event_loop.rs | 15 +++++++++------ src/platform_impl/stdweb/window.rs | 22 ++++++++++++++++------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 2827138c60..f6bdcab429 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -232,8 +232,9 @@ fn add_event(elrs: &EventLoopRunnerShared, target: &impl IE target.add_event_listener(move |event: E| { // Don't capture the event if the events loop has been destroyed - if elrs.runner.borrow().control == ControlFlow::Exit { - return; + match &*elrs.runner.borrow() { + Some(ref runner) if runner.control == ControlFlow::Exit => return, + _ => () } event.prevent_default(); @@ -248,7 +249,7 @@ impl ELRShared { fn set_listener(&self, event_handler: Box, &mut ControlFlow)>) { *self.runner.borrow_mut() = Some(EventLoopRunner { control: ControlFlow::Poll, - handling: false + handling: false, event_handler }); } @@ -257,18 +258,20 @@ impl ELRShared { // TODO: handle event buffer pub fn send_event(&self, event: Event) { let start_cause = StartCause::Poll; // TODO: this is obviously not right - self.handle_start(StartCause::Poll); + self.handle_start(start_cause); self.handle_event(event); self.handle_event(Event::EventsCleared); } fn handle_start(&self, start: StartCause) { - let is_handling = if Some(ref runner) = *self.runner.borrow() { + let is_handling = if let Some(ref runner) = *self.runner.borrow() { runner.handling } else { false }; - self.handle_event(Event::StartCause(is_handling)); + if is_handling { + self.handle_event(Event::NewEvents(start)); + } } fn handle_event(&self, event: Event) { diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index df875f03ed..2a712f1236 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -53,7 +53,8 @@ impl WindowId { pub struct Window { pub(crate) canvas: CanvasElement, pub(crate) redraw: Box, - previous_pointer: RefCell<&'static str> + previous_pointer: RefCell<&'static str>, + position: RefCell, } impl Window { @@ -79,7 +80,11 @@ impl Window { let window = Window { canvas, redraw, - previous_pointer: RefCell::new("auto") + previous_pointer: RefCell::new("auto"), + position: RefCell::new(LogicalPosition { + x: 0.0, + y: 0.0 + }) }; if let Some(dimensions) = attr.dimensions { @@ -134,12 +139,17 @@ impl Window { } pub fn get_inner_position(&self) -> Option { - // TODO - None + Some(*self.position.borrow()) } - pub fn set_position(&self, _position: LogicalPosition) { - // TODO: use CSS? + pub fn set_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] From fe5e3000628f8e72eeceae20344ad1fa9b8839b0 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Thu, 25 Apr 2019 00:02:13 -0400 Subject: [PATCH 15/27] Clean up and document the core of stdweb event handling --- src/platform_impl/stdweb/event_loop.rs | 84 ++++++++++++++++++++------ 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index f6bdcab429..d9d2da5508 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -67,7 +67,7 @@ pub struct ELRShared { struct EventLoopRunner { control: ControlFlow, - handling: bool, + is_busy: bool, event_handler: Box, &mut ControlFlow)>, } @@ -246,49 +246,93 @@ fn add_event(elrs: &EventLoopRunnerShared, target: &impl IE } 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, - handling: false, + is_busy: false, event_handler }); } - // TODO: handle event loop closures - // TODO: handle event buffer + // 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) { - let start_cause = StartCause::Poll; // TODO: this is obviously not right - self.handle_start(start_cause); - self.handle_event(event); - self.handle_event(Event::EventsCleared); - } + // If the event loop is closed, it should discard any new events + if self.closed() { + return; + } - fn handle_start(&self, start: StartCause) { - let is_handling = if let Some(ref runner) = *self.runner.borrow() { - runner.handling + 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 { - false + true // If there is no event runner yet, then there's no point in processing events }; - if is_handling { - self.handle_event(Event::NewEvents(start)); + + 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) if !runner.handling => { - runner.handling = true; - let closed = runner.control == ControlFlow::Exit; + 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; } - runner.handling = false; + + // 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) } - if self.runner.borrow().is_some() { + + // 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); } From 9f801cf79e9e17ed4220dbaa051ea80e2e6226b0 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Mon, 29 Apr 2019 15:39:43 -0400 Subject: [PATCH 16/27] Only send the request-redraw on the next animation frame --- src/platform_impl/stdweb/window.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index 2a712f1236..12fbaba5fb 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -12,7 +12,7 @@ use stdweb::{ unstable::TryInto }; use stdweb::web::{ - document, + document, window, html_element::CanvasElement, }; @@ -21,7 +21,6 @@ pub struct MonitorHandle; impl MonitorHandle { pub fn get_hidpi_factor(&self) -> f64 { - // TODO 1.0 } @@ -72,10 +71,10 @@ impl Window { register(&target.runner, &canvas); let runner = target.runner.clone(); - let redraw = Box::new(move || runner.send_event(Event::WindowEvent { + let redraw = Box::new(move || window().request_animation_frame(|| runner.send_event(Event::WindowEvent { window_id: RootWI(WindowId), event: WindowEvent::RedrawRequested - })); + }))); let window = Window { canvas, @@ -95,7 +94,6 @@ impl Window { height: 768.0, }) } - // TODO: most of these are no-op, but should they stay here just in case? window.set_min_dimensions(attr.min_dimensions); window.set_max_dimensions(attr.max_dimensions); window.set_resizable(attr.resizable); @@ -191,7 +189,6 @@ impl Window { #[inline] pub fn get_hidpi_factor(&self) -> f64 { - // TODO 1.0 } @@ -286,7 +283,7 @@ impl Window { #[inline] pub fn set_window_icon(&self, _window_icon: Option) { - // TODO: should this set the favicon? + // Currently an intentional no-op } #[inline] From 70c7382a099726e94542c57aa75b910dd614ad09 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Wed, 1 May 2019 21:20:54 -0400 Subject: [PATCH 17/27] Fix the request_animation_frame lifetimes --- src/platform_impl/stdweb/window.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index 12fbaba5fb..3008dd5879 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -71,10 +71,13 @@ impl Window { register(&target.runner, &canvas); let runner = target.runner.clone(); - let redraw = Box::new(move || window().request_animation_frame(|| runner.send_event(Event::WindowEvent { - window_id: RootWI(WindowId), - event: WindowEvent::RedrawRequested - }))); + let redraw = Box::new(move || { + let runner = runner.clone(); + window().request_animation_frame(move |_| runner.send_event(Event::WindowEvent { + window_id: RootWI(WindowId), + event: WindowEvent::RedrawRequested + })); + }); let window = Window { canvas, From 37dadab745062135284c621d45fb58c63b9ad445 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Fri, 31 May 2019 21:48:26 -0700 Subject: [PATCH 18/27] Add access to the canvas in the Window --- src/platform/mod.rs | 2 ++ src/platform/stdweb.rs | 8 ++++++++ src/platform_impl/stdweb/window.rs | 9 ++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/platform/stdweb.rs diff --git a/src/platform/mod.rs b/src/platform/mod.rs index ba494ac6d3..da780f4bd0 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -20,4 +20,6 @@ pub mod macos; pub mod unix; pub mod windows; +pub mod stdweb; + pub mod desktop; diff --git a/src/platform/stdweb.rs b/src/platform/stdweb.rs new file mode 100644 index 0000000000..4ffa7dd2bb --- /dev/null +++ b/src/platform/stdweb.rs @@ -0,0 +1,8 @@ +#![cfg(feature = "stdweb")] + +use stdweb::web::html_element::CanvasElement; + +pub trait WindowExtStdweb { + fn canvas(&self) -> CanvasElement; +} + diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index 3008dd5879..1ab8a0d295 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -1,8 +1,9 @@ use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; use event::{Event, WindowEvent}; use icon::Icon; +use platform::stdweb::WindowExtStdweb; use monitor::{MonitorHandle as RootMH}; -use window::{CreationError, MouseCursor, WindowAttributes, WindowId as RootWI}; +use window::{CreationError, MouseCursor, Window as RootWindow, WindowAttributes, WindowId as RootWI}; use super::{EventLoopWindowTarget, register}; use std::collections::VecDeque; use std::collections::vec_deque::IntoIter as VecDequeIter; @@ -317,3 +318,9 @@ impl Window { unsafe { WindowId::dummy() } } } + +impl WindowExtStdweb for RootWindow { + fn canvas(&self) -> CanvasElement { + self.window.canvas.clone() + } +} From 1409f83fb92e79aaa345bedfc9f662aa996933cf Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Fri, 31 May 2019 21:50:34 -0700 Subject: [PATCH 19/27] Add support for mouse wheel --- src/platform_impl/stdweb/event_loop.rs | 27 +++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index d9d2da5508..8ec0735564 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -1,7 +1,7 @@ use super::*; use dpi::LogicalPosition; -use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, StartCause, WindowEvent}; +use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, MouseScrollDelta, StartCause, TouchPhase, WindowEvent}; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; use window::{WindowId as RootWI}; use stdweb::{ @@ -151,12 +151,15 @@ impl EventLoop { } }); }); + + runner.send_event(Event::NewEvents(StartCause::Init)); + stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? // 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 '!' js! { - throw "Using exceptions for control flow, don't mind me"; + throw "Using exceptions for control flow, don't mind me. This isn't actually an error!"; } unreachable!(); } @@ -224,9 +227,27 @@ pub fn register(elrs: &EventLoopRunnerShared, canvas: &CanvasElem } }); }); + add_event(elrs, canvas, |elrs, event: MouseWheelEvent| { + let x = event.delta_x(); + let y = event.delta_y(); + let delta = match event.delta_mode() { + MouseWheelDeltaMode::Line => MouseScrollDelta::LineDelta(x as f32, y as f32), + MouseWheelDeltaMode::Pixel => MouseScrollDelta::PixelDelta(LogicalPosition { x, y }), + MouseWheelDeltaMode::Page => 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: &impl IEventTarget, mut handler: F) +fn add_event(elrs: &EventLoopRunnerShared, target: &impl IEventTarget, mut handler: F) where E: ConcreteEvent, F: FnMut(&EventLoopRunnerShared, E) + 'static { let elrs = elrs.clone(); From b59e3c670bf49a2c020fb432f3d6681ede0b1c00 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Fri, 14 Jun 2019 21:15:43 -0700 Subject: [PATCH 20/27] WIP --- Cargo.toml | 2 +- src/platform_impl/stdweb/event_loop.rs | 102 +++++++++++++++++-------- 2 files changed, 70 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0d7e5e0a4e..c0a2c1c0f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,5 +74,5 @@ percent-encoding = "1.0" version = "0.8" [target.'cfg(target_arch = "wasm32")'.dependencies.stdweb] -version = "0.4.17" +path = "../stdweb" optional = true diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 37b00f77fe..87eeed6c1c 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -10,6 +10,7 @@ use stdweb::{ document, event::*, html_element::CanvasElement, + TimeoutHandle, }, }; use std::cell::RefCell; @@ -17,6 +18,7 @@ use std::collections::VecDeque; use std::collections::vec_deque::IntoIter as VecDequeIter; use std::marker::PhantomData; use std::rc::Rc; +use std::time::Instant; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId(i32); @@ -66,11 +68,44 @@ pub struct ELRShared { } struct EventLoopRunner { - control: ControlFlow, + control: ControlFlowStatus, is_busy: bool, event_handler: Box, &mut ControlFlow)>, } +enum ControlFlowStatus { + WaitUntil { + timeout: TimeoutHandle, + start: Instant, + end: Instant + }, + Wait { + start: Instant, + }, + Poll { + timeout: TimeoutHandle + }, + Exit +} + +impl ControlFlowStatus { + fn to_control_flow(&self) -> ControlFlow { + match self { + ControlFlowStatus::WaitUntil { end, .. } => ControlFlow::WaitUntil(*end), + ControlFlowStatus::Wait { .. } => ControlFlow::Wait, + ControlFlowStatus::Poll { .. } => ControlFlow::Poll, + ControlFlowStatus::Exit => ControlFlow::Exit, + } + } + + fn is_exit(&self) -> bool { + match self { + ControlFlowStatus::Exit => true, + _ => false, + } + } +} + impl EventLoop { pub fn new() -> Self { EventLoop { @@ -152,8 +187,6 @@ impl EventLoop { }); }); - runner.send_event(Event::NewEvents(StartCause::Init)); - stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? // Throw an exception to break out of Rust exceution and use unreachable to tell the @@ -254,7 +287,7 @@ fn add_event(elrs: &EventLoopRunnerShared, target: &impl IE target.add_event_listener(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, + Some(ref runner) if runner.control.is_exit() => return, _ => () } @@ -271,10 +304,11 @@ impl ELRShared { // 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)>) { + // TODO: Start the poll here *self.runner.borrow_mut() = Some(EventLoopRunner { - control: ControlFlow::Poll, + control: ControlFlowStatus::Exit, is_busy: false, - event_handler + event_handler, }); } @@ -287,30 +321,32 @@ impl ELRShared { return; } + // TODO: Determine if a timeout needs to be cleared + 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); + match *self.runner.borrow() { + Some(ref runner) if !runner.is_busy => { + let mut control = runner.control.to_control_flow(); + // 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), &mut control); + self.handle_event(event, &mut control); + self.handle_event(Event::EventsCleared, &mut control); + + // TODO: integrate control flow change and set up the next iteration + + // 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, &mut control); + } + } + _ => { + self.events.borrow_mut().push_back(event); } } } @@ -318,7 +354,7 @@ impl ELRShared { // Check if the event loop is currntly closed fn closed(&self) -> bool { match *self.runner.borrow() { - Some(ref runner) => runner.control == ControlFlow::Exit, + Some(ref runner) => runner.control.is_exit(), None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed } } @@ -326,7 +362,7 @@ impl ELRShared { // 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) { + fn handle_event(&self, event: Event, control: &mut ControlFlow) { let closed = self.closed(); match *self.runner.borrow_mut() { @@ -335,11 +371,11 @@ impl ELRShared { runner.is_busy = true; // TODO: bracket this in control flow events? - (runner.event_handler)(event, &mut runner.control); - + (runner.event_handler)(event, control); + // Maintain closed state, even if the callback changes it if closed { - runner.control = ControlFlow::Exit; + *control = ControlFlow::Exit; } // An event is no longer being processed @@ -355,7 +391,7 @@ impl ELRShared { 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); + self.handle_event(event, control); } } } From 2690306f4a028c549212a9d64c1a6a3e9d2fe08f Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sun, 16 Jun 2019 21:30:05 -0700 Subject: [PATCH 21/27] Implement Poll and WaitUntil in the stdweb backend --- Cargo.toml | 7 +- src/event.rs | 4 +- src/event_loop.rs | 4 +- src/lib.rs | 1 + src/platform_impl/stdweb/event_loop.rs | 144 ++++++++++++++++++------- 5 files changed, 115 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c0a2c1c0f5..4ee842395b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ categories = ["gui"] features = ["serde"] [dependencies] +instant = "0.1" lazy_static = "1" libc = "0.2" log = "0.4" @@ -73,6 +74,6 @@ percent-encoding = "1.0" [target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies.parking_lot] version = "0.8" -[target.'cfg(target_arch = "wasm32")'.dependencies.stdweb] -path = "../stdweb" -optional = true +[target.'cfg(target_arch = "wasm32")'.dependencies] +stdweb = { path = "../stdweb", optional = true } +instant = { version = "0.1", features = ["stdweb"] } diff --git a/src/event.rs b/src/event.rs index 1d1a8fb5ba..7eaee201d0 100644 --- a/src/event.rs +++ b/src/event.rs @@ -4,7 +4,7 @@ //! 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 instant::Instant; use std::path::PathBuf; use dpi::{LogicalPosition, LogicalSize}; @@ -58,7 +58,7 @@ impl Event { } /// Describes the reason the event loop is resuming. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] 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 diff --git a/src/event_loop.rs b/src/event_loop.rs index 476e7ed5e1..91a3f912b9 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -10,7 +10,7 @@ //! [event_loop_proxy]: ./struct.EventLoopProxy.html //! [send_event]: ./struct.EventLoopProxy.html#method.send_event use std::{fmt, error}; -use std::time::Instant; +use instant::Instant; use std::ops::Deref; use platform_impl; @@ -69,7 +69,7 @@ impl fmt::Debug for EventLoopWindowTarget { /// the control flow to `Poll`. /// /// [events_cleared]: ../event/enum.Event.html#variant.EventsCleared -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] 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. diff --git a/src/lib.rs b/src/lib.rs index 7846803a52..a15664c2bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,6 +75,7 @@ //! [`LoopDestroyed`]: ./event/enum.Event.html#variant.LoopDestroyed //! [`platform`]: ./platform/index.html +extern crate instant; #[allow(unused_imports)] #[macro_use] extern crate lazy_static; diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 87eeed6c1c..822ebceaf0 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -3,6 +3,7 @@ use super::*; use dpi::LogicalPosition; use event::{DeviceId as RootDI, ElementState, Event, KeyboardInput, MouseScrollDelta, StartCause, TouchPhase, WindowEvent}; use event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoopClosed}; +use instant::{Duration, Instant}; use window::{WindowId as RootWI}; use stdweb::{ traits::*, @@ -10,15 +11,17 @@ use stdweb::{ document, event::*, html_element::CanvasElement, + window, TimeoutHandle, }, }; -use std::cell::RefCell; -use std::collections::VecDeque; -use std::collections::vec_deque::IntoIter as VecDequeIter; -use std::marker::PhantomData; -use std::rc::Rc; -use std::time::Instant; +use std::{ + cell::RefCell, + collections::{VecDeque, vec_deque::IntoIter as VecDequeIter}, + clone::Clone, + marker::PhantomData, + rc::Rc, +}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DeviceId(i32); @@ -40,31 +43,37 @@ pub struct EventLoopWindowTarget { impl EventLoopWindowTarget { fn new() -> Self { EventLoopWindowTarget { - runner: Rc::new(ELRShared { + runner: EventLoopRunnerShared(Rc::new(ELRShared { runner: RefCell::new(None), events: RefCell::new(VecDeque::new()) - }) + })) } } } #[derive(Clone)] -pub struct EventLoopProxy { +pub struct EventLoopProxy { runner: EventLoopRunnerShared } -impl EventLoopProxy { +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 EventLoopRunnerShared(Rc>); + +impl Clone for EventLoopRunnerShared { + fn clone(&self) -> Self { + EventLoopRunnerShared(self.0.clone()) + } +} pub struct ELRShared { runner: RefCell>>, - events: RefCell>>, // TODO: this may not be necessary? + events: RefCell>>, } struct EventLoopRunner { @@ -74,6 +83,7 @@ struct EventLoopRunner { } enum ControlFlowStatus { + Init, WaitUntil { timeout: TimeoutHandle, start: Instant, @@ -91,6 +101,7 @@ enum ControlFlowStatus { impl ControlFlowStatus { fn to_control_flow(&self) -> ControlFlow { match self { + ControlFlowStatus::Init => ControlFlow::Poll, // During the Init loop, the user should get Poll, the default control value ControlFlowStatus::WaitUntil { end, .. } => ControlFlow::WaitUntil(*end), ControlFlowStatus::Wait { .. } => ControlFlow::Wait, ControlFlowStatus::Poll { .. } => ControlFlow::Poll, @@ -286,7 +297,7 @@ fn add_event(elrs: &EventLoopRunnerShared, target: &impl IE target.add_event_listener(move |event: E| { // Don't capture the event if the events loop has been destroyed - match &*elrs.runner.borrow() { + match &*elrs.0.runner.borrow() { Some(ref runner) if runner.control.is_exit() => return, _ => () } @@ -299,17 +310,17 @@ fn add_event(elrs: &EventLoopRunnerShared, target: &impl IE }); } -impl ELRShared { +impl EventLoopRunnerShared { // 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)>) { - // TODO: Start the poll here - *self.runner.borrow_mut() = Some(EventLoopRunner { - control: ControlFlowStatus::Exit, + *self.0.runner.borrow_mut() = Some(EventLoopRunner { + control: ControlFlowStatus::Init, is_busy: false, event_handler, }); + self.send_event(Event::NewEvents(StartCause::Init)); } // Add an event to the event loop runner @@ -321,23 +332,46 @@ impl ELRShared { return; } - // TODO: Determine if a timeout needs to be cleared - - let start_cause = StartCause::Poll; // TODO: determine start cause - // Determine if event handling is in process, and then release the borrow on the runner - match *self.runner.borrow() { + match *self.0.runner.borrow() { Some(ref runner) if !runner.is_busy => { + let (start_cause, event_is_start) = if let Event::NewEvents(cause) = event { + (cause, true) + } else { + (match runner.control { + ControlFlowStatus::Init => StartCause::Init, + ControlFlowStatus::Poll { ref timeout } => { + timeout.clear(); + + StartCause::Poll + } + ControlFlowStatus::Wait { start } => StartCause::WaitCancelled { + start, + requested_resume: None, + }, + ControlFlowStatus::WaitUntil { start, end, ref timeout } => { + timeout.clear(); + + StartCause::WaitCancelled { + start, + requested_resume: Some(end) + } + }, + ControlFlowStatus::Exit => { return; } + }, false) + }; let mut control = runner.control.to_control_flow(); // 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), &mut control); - self.handle_event(event, &mut control); + if !event_is_start { + self.handle_event(event, &mut control); + } self.handle_event(Event::EventsCleared, &mut control); - // TODO: integrate control flow change and set up the next iteration + self.apply_control_flow(control); // If the event loop is closed, it has been closed this iteration and now the closing // event should be emitted @@ -346,31 +380,22 @@ impl ELRShared { } } _ => { - self.events.borrow_mut().push_back(event); + self.0.events.borrow_mut().push_back(event); } } } - // Check if the event loop is currntly closed - fn closed(&self) -> bool { - match *self.runner.borrow() { - Some(ref runner) => runner.control.is_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, control: &mut ControlFlow) { let closed = self.closed(); - match *self.runner.borrow_mut() { + match *self.0.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, control); // Maintain closed state, even if the callback changes it @@ -383,18 +408,61 @@ impl ELRShared { } // 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) + _ => self.0.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() { + if !closed && self.0.runner.borrow().is_some() { // Take an event out of the queue and handle it - if let Some(event) = self.events.borrow_mut().pop_front() { + if let Some(event) = self.0.events.borrow_mut().pop_front() { self.handle_event(event, control); } } } + // Apply the new ControlFlow that has been selected by the user + // Start any necessary timeouts etc + fn apply_control_flow(&self, control_flow: ControlFlow) { + let control_flow_status = match control_flow { + ControlFlow::Poll => { + let cloned = self.clone(); + ControlFlowStatus::Poll { + timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), 0) + } + } + ControlFlow::Wait => ControlFlowStatus::Wait { start: Instant::now() }, + ControlFlow::WaitUntil(end) => { + let cloned = self.clone(); + let start = Instant::now(); + let delay = if end <= start { + Duration::from_millis(0) + } else { + end - start + }; + ControlFlowStatus::WaitUntil { + start, + end, + timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), delay.as_millis() as u32) + } + } + ControlFlow::Exit => ControlFlowStatus::Exit, + }; + + match *self.0.runner.borrow_mut() { + Some(ref mut runner) => { + runner.control = control_flow_status; + } + None => () + } + } + + // Check if the event loop is currntly closed + fn closed(&self) -> bool { + match *self.0.runner.borrow() { + Some(ref runner) => runner.control.is_exit(), + None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed + } + } } From 182beb4f8b47aedfb8c9b31cc5571d67f4946a82 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sun, 16 Jun 2019 21:34:54 -0700 Subject: [PATCH 22/27] Indicate that I will be maintaing the stdweb backend --- CONTRIBUTING.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8a0c7832a..7de04ab8d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,9 +46,10 @@ This table summarizes who can be contacted in which case, with the following leg - `T` - Tester: has the ability of testing the platform - ` `: knows nothing of this platform -| Platform | Windows | macOS | X11 | Wayland | Android | iOS | Emscripten | -| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | -| @mitchmindtree | T | | T | T | | | | -| @Osspial | M | | T | T | T | | T | -| @vberger | | | T | M | | | | -| @mtak- | | T | | | T | M | | +| Platform | Windows | macOS | X11 | Wayland | Android | iOS | Emscripten | Stdweb | +| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | +| @mitchmindtree | T | | T | T | | | | | +| @Osspial | M | | T | T | T | | T | | +| @vberger | | | T | M | | | | | +| @mtak- | | T | | | T | M | | | +| @ryanisacg | T | T | | | | | | M | From b571362bf116f069521619f6b7312f3545ee870f Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Thu, 20 Jun 2019 21:46:01 -0700 Subject: [PATCH 23/27] Fix a panic due to double-borrow --- Cargo.toml | 3 ++ src/platform_impl/stdweb/event_loop.rs | 49 +++++++++++++++----------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4ee842395b..5127f78fa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,3 +77,6 @@ version = "0.8" [target.'cfg(target_arch = "wasm32")'.dependencies] stdweb = { path = "../stdweb", optional = true } instant = { version = "0.1", features = ["stdweb"] } + +[patch.crates-io] +stdweb = { path = "../stdweb" } diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 822ebceaf0..97d582ff12 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -333,9 +333,9 @@ impl EventLoopRunnerShared { } // Determine if event handling is in process, and then release the borrow on the runner - match *self.0.runner.borrow() { + let (start_cause, event_is_start) = match *self.0.runner.borrow() { Some(ref runner) if !runner.is_busy => { - let (start_cause, event_is_start) = if let Event::NewEvents(cause) = event { + if let Event::NewEvents(cause) = event { (cause, true) } else { (match runner.control { @@ -359,29 +359,30 @@ impl EventLoopRunnerShared { }, ControlFlowStatus::Exit => { return; } }, false) - }; - let mut control = runner.control.to_control_flow(); - // 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), &mut control); - if !event_is_start { - self.handle_event(event, &mut control); - } - self.handle_event(Event::EventsCleared, &mut control); - - self.apply_control_flow(control); - - // 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, &mut control); } } _ => { + // Events are currently being handled, so queue this one and don't try to + // double-process the event queue self.0.events.borrow_mut().push_back(event); + return; } + }; + let mut control = self.current_control_flow(); + // 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), &mut control); + if !event_is_start { + self.handle_event(event, &mut control); + } + self.handle_event(Event::EventsCleared, &mut control); + self.apply_control_flow(control); + // 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, &mut control); } } @@ -464,5 +465,13 @@ impl EventLoopRunnerShared { None => false, // If the event loop is None, it has not been intialised yet, so it cannot be closed } } + + // Get the current control flow state + fn current_control_flow(&self) -> ControlFlow { + match *self.0.runner.borrow() { + Some(ref runner) => runner.control.to_control_flow(), + None => ControlFlow::Poll, + } + } } From cf28751ae3d1fcf39aa25fe4d82cd654a56948e8 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sun, 23 Jun 2019 14:38:16 -0700 Subject: [PATCH 24/27] Remove unnecessary set-to-wait in example --- examples/window.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/window.rs b/examples/window.rs index b33b5984c1..eb87fa81cf 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -23,7 +23,7 @@ fn main() { event: WindowEvent::CloseRequested, .. } => *control_flow = ControlFlow::Exit, - _ => *control_flow = ControlFlow::Wait, + _ => () } }); } From a0f280e71ffb94f2cb1c4cbf768f260cfaf0a485 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Sun, 23 Jun 2019 14:38:35 -0700 Subject: [PATCH 25/27] Update how timeouts are cleared to avoid possible double-clearing --- src/platform_impl/stdweb/event_loop.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 97d582ff12..c7b2f85bf5 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -340,18 +340,14 @@ impl EventLoopRunnerShared { } else { (match runner.control { ControlFlowStatus::Init => StartCause::Init, - ControlFlowStatus::Poll { ref timeout } => { - timeout.clear(); - + ControlFlowStatus::Poll { .. } => { StartCause::Poll } ControlFlowStatus::Wait { start } => StartCause::WaitCancelled { start, requested_resume: None, }, - ControlFlowStatus::WaitUntil { start, end, ref timeout } => { - timeout.clear(); - + ControlFlowStatus::WaitUntil { start, end, .. } => { StartCause::WaitCancelled { start, requested_resume: Some(end) @@ -398,7 +394,7 @@ impl EventLoopRunnerShared { runner.is_busy = true; (runner.event_handler)(event, control); - + // Maintain closed state, even if the callback changes it if closed { *control = ControlFlow::Exit; @@ -425,11 +421,11 @@ impl EventLoopRunnerShared { // Apply the new ControlFlow that has been selected by the user // Start any necessary timeouts etc fn apply_control_flow(&self, control_flow: ControlFlow) { - let control_flow_status = match control_flow { + let mut control_flow_status = match control_flow { ControlFlow::Poll => { let cloned = self.clone(); ControlFlowStatus::Poll { - timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), 0) + timeout: window().set_clearable_timeout(move || cloned.send_event(Event::NewEvents(StartCause::Poll)), 1) } } ControlFlow::Wait => ControlFlowStatus::Wait { start: Instant::now() }, @@ -452,7 +448,15 @@ impl EventLoopRunnerShared { match *self.0.runner.borrow_mut() { Some(ref mut runner) => { - runner.control = control_flow_status; + // Put the new control flow status in the runner, and take out the old one + // This way we can safely take ownership of the TimeoutHandle and clear it, + // so that we don't get 'ghost' invocations of Poll or WaitUntil from earlier + // set_timeout invocations + std::mem::swap(&mut runner.control, &mut control_flow_status); + match control_flow_status { + ControlFlowStatus::Poll { timeout } | ControlFlowStatus::WaitUntil { timeout, .. } => timeout.clear(), + _ => (), + } } None => () } From 7bbc82963eb66e69fd0e4187f1277551cc65e3ac Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Mon, 8 Jul 2019 20:33:04 -0700 Subject: [PATCH 26/27] Fix a few warnings --- src/event.rs | 1 - src/platform_impl/stdweb/event.rs | 2 +- src/platform_impl/stdweb/event_loop.rs | 2 +- src/platform_impl/stdweb/window.rs | 4 ---- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/event.rs b/src/event.rs index ed5b1c4ef0..236e7f78c0 100644 --- a/src/event.rs +++ b/src/event.rs @@ -7,7 +7,6 @@ use instant::Instant; use std::path::PathBuf; -use stdweb::js; use crate::{ dpi::{LogicalPosition, LogicalSize}, diff --git a/src/platform_impl/stdweb/event.rs b/src/platform_impl/stdweb/event.rs index ed59160cc6..259840bb89 100644 --- a/src/platform_impl/stdweb/event.rs +++ b/src/platform_impl/stdweb/event.rs @@ -1,5 +1,5 @@ use stdweb::{ - js, _js_impl, + js, JsSerialize, web::event::{IKeyboardEvent, IMouseEvent}, unstable::TryInto diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 11e4af69f7..60cb4a70d4 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -6,7 +6,7 @@ use crate::event_loop::{ControlFlow, EventLoopWindowTarget as RootELW, EventLoop use instant::{Duration, Instant}; use crate::window::{WindowId as RootWI}; use stdweb::{ - js, _js_impl, + js, traits::*, web::{ document, diff --git a/src/platform_impl/stdweb/window.rs b/src/platform_impl/stdweb/window.rs index d7ccf97f9f..dc2b68ff23 100644 --- a/src/platform_impl/stdweb/window.rs +++ b/src/platform_impl/stdweb/window.rs @@ -30,10 +30,6 @@ impl MonitorHandle { unimplemented!(); } - pub fn dimensions(&self) -> PhysicalSize { - unimplemented!(); - } - pub fn name(&self) -> Option { unimplemented!(); } From c76ab65beacdd635bb64255d2b6ce5df0b244c20 Mon Sep 17 00:00:00 2001 From: Ryan Goldstein Date: Mon, 8 Jul 2019 20:51:35 -0700 Subject: [PATCH 27/27] Add on-close event listening --- src/platform_impl/stdweb/event_loop.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/platform_impl/stdweb/event_loop.rs b/src/platform_impl/stdweb/event_loop.rs index 60cb4a70d4..18dd4afa67 100644 --- a/src/platform_impl/stdweb/event_loop.rs +++ b/src/platform_impl/stdweb/event_loop.rs @@ -147,6 +147,8 @@ impl EventLoop { runner.set_listener(Box::new(move |evt, ctrl| event_handler(evt, &relw, ctrl))); let document = &document(); + let window = &window(); + add_event(&runner, document, |elrs, _: BlurEvent| { elrs.send_event(Event::WindowEvent { window_id: RootWI(WindowId), @@ -198,6 +200,14 @@ impl EventLoop { } }); }); + add_event(&runner, window, |elrs, _event: UnloadEvent| { + elrs.send_event(Event::LoopDestroyed); + // Mark the event loop as exited, so new events are chucked out + match *elrs.0.runner.borrow_mut() { + Some(ref mut runner) => runner.control = ControlFlowStatus::Exit, + None => () + } + }); stdweb::event_loop(); // TODO: this is only necessary for stdweb emscripten, should it be here? @@ -371,6 +381,8 @@ impl EventLoopRunnerShared { // 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), &mut control); + // In the case the event is just a NewEvents (a Poll has completed or a WaitUntil expired) + // don't run the event handler twice. if !event_is_start { self.handle_event(event, &mut control); }