From b0552cb98e627f8a21e107ee56c9b731579c4838 Mon Sep 17 00:00:00 2001 From: "Nathan E. Egge" Date: Fri, 12 Dec 2014 10:19:04 -0500 Subject: [PATCH] Sending key events through script task before processing them in the compositor. Fixes #4163 --- components/compositing/compositor.rs | 5 ++ components/compositing/compositor_task.rs | 10 +++ components/compositing/headless.rs | 4 +- components/compositing/windowing.rs | 4 +- components/msg/compositor_msg.rs | 2 + components/script/dom/htmlinputelement.rs | 5 +- components/script/script_task.rs | 4 + ports/cef/window.rs | 5 ++ ports/glfw/window.rs | 61 +++++++------- ports/glutin/window.rs | 97 ++++++++++++++++------- 10 files changed, 131 insertions(+), 66 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 0466b05540d..befa0fce662 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -4,6 +4,7 @@ use compositor_layer::{CompositorData, CompositorLayer, DoesntWantScrollEvents}; use compositor_layer::{WantsScrollEvents}; +use compositor_task; use compositor_task::{ChangePageLoadData, ChangePageTitle, ChangePaintState, ChangeReadyState}; use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver}; use compositor_task::{CompositorTask, CreateOrUpdateDescendantLayer, CreateOrUpdateRootLayer}; @@ -336,6 +337,10 @@ impl IOCompositor { } } + (compositor_task::KeyEvent(key, modified), NotShuttingDown) => { + self.window.handle_key(key, modified); + } + // When we are shutting_down, we need to avoid performing operations // such as Paint that may crash because we have begun tearing down // the rest of our resources. diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index e7d851ceaf6..733637b1b95 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -20,6 +20,7 @@ use layers::layers::LayerBufferSet; use servo_msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState}; use servo_msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy}; use servo_msg::constellation_msg::{ConstellationChan, LoadData, PipelineId}; +use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, Pressed}; use servo_util::memory::MemoryProfilerChan; use servo_util::time::TimeProfilerChan; use std::comm::{channel, Sender, Receiver}; @@ -85,6 +86,12 @@ impl ScriptListener for Box { fn set_title(&mut self, pipeline_id: PipelineId, title: Option) { self.send(ChangePageTitle(pipeline_id, title)) } + + fn send_key_event(&mut self, key: Key, state: KeyState, modifiers: KeyModifiers) { + if state == Pressed { + self.send(KeyEvent(key, modifiers)); + } + } } /// Information about each layer that the compositor keeps. @@ -204,6 +211,8 @@ pub enum Msg { /// Indicates that the scrolling timeout with the given starting timestamp has happened and a /// composite should happen. (See the `scrolling` module.) ScrollTimeout(u64), + /// Sends an unconsumed key event back to the compositor. + KeyEvent(Key, KeyModifiers), } impl Show for Msg { @@ -226,6 +235,7 @@ impl Show for Msg { FrameTreeUpdateMsg(..) => write!(f, "FrameTreeUpdateMsg"), LoadComplete => write!(f, "LoadComplete"), ScrollTimeout(..) => write!(f, "ScrollTimeout"), + KeyEvent(..) => write!(f, "KeyEvent"), } } } diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 7fc1df7dbe6..c9cf6a25132 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -6,7 +6,7 @@ use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpda use compositor_task::{Exit, ChangeReadyState, LoadComplete, Paint, ScrollFragmentPoint, SetIds}; use compositor_task::{SetLayerOrigin, ShutdownComplete, ChangePaintState, PaintMsgDiscarded}; use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout, ChangePageTitle}; -use compositor_task::{ChangePageLoadData, FrameTreeUpdateMsg}; +use compositor_task::{ChangePageLoadData, FrameTreeUpdateMsg, KeyEvent}; use windowing::WindowEvent; use geom::scale_factor::ScaleFactor; @@ -106,7 +106,7 @@ impl CompositorEventListener for NullCompositor { SetLayerOrigin(..) | Paint(..) | ChangeReadyState(..) | ChangePaintState(..) | ScrollFragmentPoint(..) | LoadComplete | PaintMsgDiscarded(..) | ScrollTimeout(..) | ChangePageTitle(..) | - ChangePageLoadData(..) => () + ChangePageLoadData(..) | KeyEvent(..) => () } true } diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 339a5fe789d..fdfc93c559e 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -123,5 +123,7 @@ pub trait WindowMethods { /// some type of platform-specific graphics context current. Returns true if the composite may /// proceed and false if it should not. fn prepare_for_composite(&self) -> bool; -} + /// Process a key event. + fn handle_key(&self, key: Key, mods: KeyModifiers); +} diff --git a/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs index dad31c08976..32717ec8f17 100644 --- a/components/msg/compositor_msg.rs +++ b/components/msg/compositor_msg.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use azure::azure_hl::Color; +use constellation_msg::{Key, KeyState, KeyModifiers}; use geom::point::Point2D; use geom::rect::Rect; use layers::platform::surface::NativeGraphicsMetadata; @@ -115,4 +116,5 @@ pub trait ScriptListener { fn set_title(&mut self, pipeline_id: PipelineId, new_title: Option); fn close(&mut self); fn dup(&mut self) -> Box; + fn send_key_event(&mut self, key: Key, state: KeyState, modifiers: KeyModifiers); } diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 1fee2c82627..4e211bde251 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -505,11 +505,12 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> { } else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() && (self.input_type.get() == InputText || self.input_type.get() == InputPassword) { let keyevent: Option> = KeyboardEventCast::to_ref(event); - keyevent.map(|event| { - match self.textinput.borrow_mut().handle_keydown(event) { + keyevent.map(|keyevent| { + match self.textinput.borrow_mut().handle_keydown(keyevent) { TriggerDefaultAction => (), DispatchInput => { self.force_relayout(); + event.PreventDefault(); } Nothing => (), } diff --git a/components/script/script_task.rs b/components/script/script_task.rs index b4fdaa6f833..dd29d9e31d3 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -946,6 +946,10 @@ impl ScriptTask { // TODO: if keypress event is canceled, prevent firing input events } + if !prevented { + self.compositor.borrow_mut().send_key_event(key, state, modifiers); + } + // This behavior is unspecced // We are supposed to dispatch synthetic click activation for Space and/or Return, // however *when* we do it is up to us diff --git a/ports/cef/window.rs b/ports/cef/window.rs index f677cf036d8..271fc33c7af 100644 --- a/ports/cef/window.rs +++ b/ports/cef/window.rs @@ -21,6 +21,7 @@ use gleam::gl; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; use libc::{c_char, c_void}; +use servo_msg::constellation_msg::{Key, KeyModifiers}; use servo_msg::compositor_msg::{Blank, FinishedLoading, Loading, PerformingLayout, PaintState}; use servo_msg::compositor_msg::{ReadyState}; use servo_msg::constellation_msg::LoadData; @@ -260,6 +261,10 @@ impl WindowMethods for Window { let frame = frame.downcast(); *frame.url.borrow_mut() = load_data.url.to_string() } + + fn handle_key(&self, _: Key, _: KeyModifiers) { + // TODO(negge) + } } struct CefCompositorProxy { diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs index d391b274f03..450e8e371f6 100644 --- a/ports/glfw/window.rs +++ b/ports/glfw/window.rs @@ -26,6 +26,7 @@ use libc::c_int; use msg::compositor_msg::{Blank, FinishedLoading, IdlePaintState, Loading, PaintState}; use msg::compositor_msg::{PaintingPaintState, PerformingLayout, ReadyState}; use msg::constellation_msg::{mod, LoadData}; +use msg::constellation_msg::{Key, KeyModifiers, KeyEscape, KeyEqual, KeyMinus, KeyBackspace, KeyPageUp, KeyPageDown, CONTROL, SHIFT}; use std::cell::{Cell, RefCell}; use std::comm::Receiver; use std::rc::Rc; @@ -206,6 +207,35 @@ impl WindowMethods for Window { box receiver as Box) } + + /// Helper function to handle keyboard events. + fn handle_key(&self, key: Key, mods: KeyModifiers) { + match key { + KeyEscape => self.glfw_window.set_should_close(true), + KeyEqual if mods.contains(CONTROL) => { // Ctrl-+ + self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1)); + } + KeyMinus if mods.contains(CONTROL) => { // Ctrl-- + self.event_queue.borrow_mut().push(ZoomWindowEvent(1.0/1.1)); + } + KeyBackspace if mods.contains(SHIFT) => { // Shift-Backspace + self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward)); + } + KeyBackspace => { // Backspace + self.event_queue.borrow_mut().push(NavigationWindowEvent(Back)); + } + KeyPageDown => { + let (_, height) = self.glfw_window.get_size(); + self.scroll_window(0.0, -height as f32); + } + KeyPageUp => { + let (_, height) = self.glfw_window.get_size(); + self.scroll_window(0.0, height as f32); + } + _ => {} + } + } + fn prepare_for_composite(&self) -> bool { true } @@ -221,9 +251,6 @@ impl Window { fn handle_window_event(&self, window: &glfw::Window, event: glfw::WindowEvent) { match event { glfw::KeyEvent(key, _, action, mods) => { - if action == glfw::Press { - self.handle_key(key, mods); - } let key = glfw_key_to_script_key(key); let state = match action { glfw::Press => constellation_msg::Pressed, @@ -333,34 +360,6 @@ impl Window { } } - /// Helper function to handle keyboard events. - fn handle_key(&self, key: glfw::Key, mods: glfw::Modifiers) { - match key { - glfw::KeyEscape => self.glfw_window.set_should_close(true), - glfw::KeyEqual if mods.contains(glfw::Control) => { // Ctrl-+ - self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1)); - } - glfw::KeyMinus if mods.contains(glfw::Control) => { // Ctrl-- - self.event_queue.borrow_mut().push(ZoomWindowEvent(1.0/1.1)); - } - glfw::KeyBackspace if mods.contains(glfw::Shift) => { // Shift-Backspace - self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward)); - } - glfw::KeyBackspace => { // Backspace - self.event_queue.borrow_mut().push(NavigationWindowEvent(Back)); - } - glfw::KeyPageDown => { - let (_, height) = self.glfw_window.get_size(); - self.scroll_window(0.0, -height as f32); - } - glfw::KeyPageUp => { - let (_, height) = self.glfw_window.get_size(); - self.scroll_window(0.0, height as f32); - } - _ => {} - } - } - /// Helper function to handle a click fn handle_mouse(&self, button: glfw::MouseButton, action: glfw::Action, x: c_int, y: c_int) { // FIXME(tkuehn): max pixel dist should be based on pixel density diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index a7e1eebc24c..0cbc74312e8 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -5,7 +5,7 @@ //! A windowing implementation using glutin. use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver}; -use compositing::windowing::{WindowEvent, WindowMethods}; +use compositing::windowing::{WindowEvent, WindowMethods, KeyEvent}; use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent}; use compositing::windowing::{MouseWindowEventClass, MouseWindowMoveEventClass, ScrollWindowEvent}; use compositing::windowing::{ZoomWindowEvent, PinchZoomWindowEvent, NavigationWindowEvent}; @@ -17,6 +17,8 @@ use geom::scale_factor::ScaleFactor; use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; +use msg::constellation_msg; +use msg::constellation_msg::{Key, KeyEscape, KeyEqual, KeyMinus, KeyBackspace, KeyPageUp, KeyPageDown, CONTROL, SHIFT, ALT}; use msg::compositor_msg::{IdlePaintState, PaintState, PaintingPaintState}; use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState}; use msg::constellation_msg::LoadData; @@ -253,6 +255,33 @@ impl WindowMethods for Window { display: GetCurrentDisplay(), } } + + /// Helper function to handle keyboard events. + fn handle_key(&self, key: Key, mods: constellation_msg::KeyModifiers) { + match key { + // TODO(negge): handle window close event + KeyEscape => {}, + KeyEqual if mods.contains(CONTROL) => { // Ctrl-+ + self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1)); + } + KeyMinus if mods.contains(CONTROL) => { // Ctrl-- + self.event_queue.borrow_mut().push(ZoomWindowEvent(1.0/1.1)); + } + KeyBackspace if mods.contains(SHIFT) => { // Shift-Backspace + self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward)); + } + KeyBackspace => { // Backspace + self.event_queue.borrow_mut().push(NavigationWindowEvent(Back)); + } + KeyPageDown => { + self.scroll_window(0.0, -self.framebuffer_size().as_f32().to_untyped().height); + } + KeyPageUp => { + self.scroll_window(0.0, self.framebuffer_size().as_f32().to_untyped().height); + } + _ => {} + } + } } impl Window { @@ -293,6 +322,33 @@ impl Window { } } +fn glutin_mods_to_script_mods(modifiers: KeyModifiers) -> constellation_msg::KeyModifiers { + let mut result = constellation_msg::KeyModifiers::from_bits(0).unwrap(); + if modifiers.intersects(LEFT_SHIFT | RIGHT_SHIFT) { + result.insert(SHIFT); + } + if modifiers.intersects(LEFT_CONTROL | RIGHT_CONTROL) { + result.insert(CONTROL); + } + if modifiers.intersects(LEFT_ALT | RIGHT_ALT) { + result.insert(ALT); + } + result +} + +fn glutin_key_to_script_key(key: glutin::VirtualKeyCode) -> Result { + // TODO(negge): add more key mappings + match key { + glutin::Escape => Ok(KeyEscape), + glutin::Equals => Ok(KeyEqual), + glutin::Minus => Ok(KeyMinus), + glutin::Back => Ok(KeyBackspace), + glutin::PageDown => Ok(KeyPageDown), + glutin::PageUp => Ok(KeyPageUp), + _ => Err(()), + } +} + impl Window { fn handle_window_event(&self, event: glutin::Event) -> bool { match event { @@ -307,7 +363,16 @@ impl Window { (_, glutin::RShift) => self.toggle_modifier(RIGHT_SHIFT), (_, glutin::LAlt) => self.toggle_modifier(LEFT_ALT), (_, glutin::RAlt) => self.toggle_modifier(RIGHT_ALT), - (glutin::Pressed, key_code) => return self.handle_key(key_code), + (glutin::Pressed, key_code) => { + match glutin_key_to_script_key(key_code) { + Ok(key) => { + let state = constellation_msg::Pressed; + let modifiers = glutin_mods_to_script_mods(self.key_modifiers.get()); + self.event_queue.borrow_mut().push(KeyEvent(key, state, modifiers)); + } + _ => {} + } + } (_, _) => {} } } @@ -371,34 +436,6 @@ impl Window { self.event_queue.borrow_mut().push(event); } - /// Helper function to handle keyboard events. - fn handle_key(&self, key: glutin::VirtualKeyCode) -> bool { - match key { - glutin::Escape => return true, - glutin::Equals if self.ctrl_pressed() => { // Ctrl-+ - self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1)); - } - glutin::Minus if self.ctrl_pressed() => { // Ctrl-- - self.event_queue.borrow_mut().push(ZoomWindowEvent(1.0/1.1)); - } - glutin::Back if self.shift_pressed() => { // Shift-Backspace - self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward)); - } - glutin::Back => { // Backspace - self.event_queue.borrow_mut().push(NavigationWindowEvent(Back)); - } - glutin::PageDown => { - self.scroll_window(0.0, -self.framebuffer_size().as_f32().to_untyped().height); - } - glutin::PageUp => { - self.scroll_window(0.0, self.framebuffer_size().as_f32().to_untyped().height); - } - _ => {} - } - - false - } - /// Helper function to handle a click fn handle_mouse(&self, button: glutin::MouseButton, action: glutin::ElementState, x: int, y: int) { // FIXME(tkuehn): max pixel dist should be based on pixel density