| // Copyright 2015 The Cobalt Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "cobalt/input/input_device_manager_desktop.h" | |
| #include <cmath> | |
| #include <string> | |
| #include "base/time/time.h" | |
| #include "cobalt/base/token.h" | |
| #include "cobalt/base/tokens.h" | |
| #include "cobalt/dom/input_event.h" | |
| #include "cobalt/dom/input_event_init.h" | |
| #include "cobalt/dom/keyboard_event.h" | |
| #include "cobalt/dom/keyboard_event_init.h" | |
| #include "cobalt/dom/pointer_event.h" | |
| #include "cobalt/dom/pointer_event_init.h" | |
| #include "cobalt/dom/wheel_event.h" | |
| #include "cobalt/dom/wheel_event_init.h" | |
| #include "cobalt/input/create_default_camera_3d.h" | |
| #include "cobalt/input/input_poller_impl.h" | |
| #include "cobalt/overlay_info/overlay_info_registry.h" | |
| #include "cobalt/system_window/input_event.h" | |
| #include "cobalt/web/event.h" | |
| #include "cobalt/web/event_init.h" | |
| namespace cobalt { | |
| namespace input { | |
| namespace { | |
| void UpdateEventInit(const system_window::InputEvent* input_event, | |
| web::EventInit* event) { | |
| if (input_event->timestamp() != 0) { | |
| // Convert SbTimeMonotonic to DOMTimeStamp. | |
| event->set_time_stamp( | |
| cobalt::web::Event::GetEventTime(input_event->timestamp())); | |
| } | |
| } | |
| } // namespace | |
| InputDeviceManagerDesktop::InputDeviceManagerDesktop( | |
| const KeyboardEventCallback& keyboard_event_callback, | |
| const PointerEventCallback& pointer_event_callback, | |
| const WheelEventCallback& wheel_event_callback, | |
| const InputEventCallback& input_event_callback, | |
| system_window::SystemWindow* system_window) | |
| : system_window_(system_window), | |
| system_window_input_event_callback_( | |
| base::Bind(&InputDeviceManagerDesktop::HandleSystemWindowInputEvent, | |
| base::Unretained(this))), | |
| input_event_callback_(input_event_callback), | |
| keypress_generator_filter_(keyboard_event_callback), | |
| pointer_event_callback_(pointer_event_callback), | |
| wheel_event_callback_(wheel_event_callback) { | |
| input_poller_ = new InputPollerImpl(); | |
| DCHECK(system_window_); | |
| camera_3d_ = | |
| CreatedDefaultCamera3D(system_window->GetSbWindow(), input_poller_); | |
| if (system_window_) { | |
| // Add this object's keyboard event callback to the system window. | |
| system_window_->event_dispatcher()->AddEventCallback( | |
| system_window::InputEvent::TypeId(), | |
| system_window_input_event_callback_); | |
| } | |
| } | |
| InputDeviceManagerDesktop::~InputDeviceManagerDesktop() { | |
| // If we have an associated system window, remove our callback from it. | |
| if (system_window_) { | |
| system_window_->event_dispatcher()->RemoveEventCallback( | |
| system_window::InputEvent::TypeId(), | |
| system_window_input_event_callback_); | |
| } | |
| } | |
| namespace { | |
| COMPILE_ASSERT(static_cast<uint32_t>(kSbKeyModifiersNone) == | |
| system_window::InputEvent::kNoModifier && | |
| static_cast<uint32_t>(kSbKeyModifiersAlt) == | |
| system_window::InputEvent::kAltKey && | |
| static_cast<uint32_t>(kSbKeyModifiersCtrl) == | |
| system_window::InputEvent::kCtrlKey && | |
| static_cast<uint32_t>(kSbKeyModifiersMeta) == | |
| system_window::InputEvent::kMetaKey && | |
| static_cast<uint32_t>(kSbKeyModifiersShift) == | |
| system_window::InputEvent::kShiftKey, | |
| Mismatched_modifier_enums); | |
| COMPILE_ASSERT(static_cast<uint32_t>(kSbKeyModifiersPointerButtonLeft) == | |
| system_window::InputEvent::kLeftButton && | |
| static_cast<uint32_t>(kSbKeyModifiersPointerButtonRight) == | |
| system_window::InputEvent::kRightButton && | |
| static_cast<uint32_t>(kSbKeyModifiersPointerButtonMiddle) == | |
| system_window::InputEvent::kMiddleButton && | |
| static_cast<uint32_t>(kSbKeyModifiersPointerButtonBack) == | |
| system_window::InputEvent::kBackButton && | |
| static_cast<uint32_t>(kSbKeyModifiersPointerButtonForward) == | |
| system_window::InputEvent::kForwardButton, | |
| Mismatched_modifier_enums); | |
| void UpdateEventModifierInit(const system_window::InputEvent* input_event, | |
| dom::EventModifierInit* event) { | |
| const uint32_t modifiers = input_event->modifiers(); | |
| event->set_ctrl_key(modifiers & system_window::InputEvent::kCtrlKey); | |
| event->set_shift_key(modifiers & system_window::InputEvent::kShiftKey); | |
| event->set_alt_key(modifiers & system_window::InputEvent::kAltKey); | |
| event->set_meta_key(modifiers & system_window::InputEvent::kMetaKey); | |
| } | |
| void UpdateMouseEventInitButtons(const system_window::InputEvent* input_event, | |
| dom::MouseEventInit* event) { | |
| // The value of the button attribute MUST be as follows: | |
| // https://www.w3.org/TR/uievents/#ref-for-dom-mouseevent-button-1 | |
| switch (input_event->key_code()) { | |
| case kSbKeyMouse1: | |
| // 0 MUST indicate the primary button of the device (in general, the left | |
| // button or the only button on single-button devices, used to activate a | |
| // user interface control or select text) or the un-initialized value. | |
| event->set_button(0); | |
| break; | |
| case kSbKeyMouse2: | |
| // 1 MUST indicate the auxiliary button (in general, the middle button, | |
| // often combined with a mouse wheel). | |
| event->set_button(1); | |
| break; | |
| case kSbKeyMouse3: | |
| // 2 MUST indicate the secondary button (in general, the right button, | |
| // often used to display a context menu). | |
| event->set_button(2); | |
| break; | |
| default: | |
| break; | |
| } | |
| // The value of the buttons attribute MUST be as follows: | |
| // https://www.w3.org/TR/uievents/#ref-for-dom-mouseevent-buttons-3 | |
| // 0 MUST indicate no button is currently active. | |
| uint32 modifiers = input_event->modifiers(); | |
| uint16 buttons = 0; | |
| if (modifiers & system_window::InputEvent::kLeftButton) { | |
| // 1 MUST indicate the primary button of the device (in general, the left | |
| // button or the only button on single-button devices, used to activate a | |
| // user interface control or select text). | |
| buttons |= 1; | |
| } | |
| if (modifiers & system_window::InputEvent::kRightButton) { | |
| // 2 MUST indicate the secondary button (in general, the right button, often | |
| // used to display a context menu), if present. | |
| buttons |= 2; | |
| } | |
| if (modifiers & system_window::InputEvent::kMiddleButton) { | |
| // 4 MUST indicate the auxiliary button (in general, the middle button, | |
| // often combined with a mouse wheel). | |
| buttons |= 4; | |
| } | |
| // The buttons attribute reflects the state of the mouse's buttons for any | |
| // MouseEvent object (while it is being dispatched). | |
| // https://www.w3.org/TR/2016/WD-uievents-20160804/#ref-for-dom-mouseevent-buttons-2 | |
| switch (input_event->type()) { | |
| case system_window::InputEvent::kTouchpadDown: | |
| case system_window::InputEvent::kTouchscreenDown: | |
| case system_window::InputEvent::kPointerDown: | |
| // For 'down' events, ensure that the buttons state includes the currently | |
| // reported button press. | |
| buttons |= 1 << event->button(); | |
| break; | |
| case system_window::InputEvent::kTouchpadUp: | |
| case system_window::InputEvent::kTouchscreenUp: | |
| case system_window::InputEvent::kPointerUp: | |
| // For 'up' events, ensure that the buttons state excludes the currently | |
| // reported button press. | |
| buttons &= ~(1 << event->button()); | |
| break; | |
| case system_window::InputEvent::kKeyDown: | |
| case system_window::InputEvent::kKeyUp: | |
| case system_window::InputEvent::kKeyMove: | |
| case system_window::InputEvent::kInput: | |
| case system_window::InputEvent::kPointerMove: | |
| case system_window::InputEvent::kTouchpadMove: | |
| case system_window::InputEvent::kTouchscreenMove: | |
| case system_window::InputEvent::kWheel: | |
| break; | |
| } | |
| event->set_buttons(buttons); | |
| } | |
| void UpdateMouseEventInit(const system_window::InputEvent* input_event, | |
| dom::MouseEventInit* mouse_event) { | |
| UpdateEventModifierInit(input_event, mouse_event); | |
| UpdateMouseEventInitButtons(input_event, mouse_event); | |
| const math::PointF& position = input_event->position(); | |
| mouse_event->set_screen_x(static_cast<float>(position.x())); | |
| mouse_event->set_screen_y(static_cast<float>(position.y())); | |
| mouse_event->set_client_x(static_cast<float>(position.x())); | |
| mouse_event->set_client_y(static_cast<float>(position.y())); | |
| } | |
| // Returns the value or the default_value when value is NaN. | |
| float value_or(float value, float default_value) { | |
| return std::isnan(value) ? default_value : value; | |
| } | |
| } // namespace | |
| void InputDeviceManagerDesktop::HandleKeyboardEvent( | |
| bool is_key_down, const system_window::InputEvent* input_event, | |
| int key_code) { | |
| base::Token type = | |
| is_key_down ? base::Tokens::keydown() : base::Tokens::keyup(); | |
| dom::KeyboardEvent::KeyLocationCode location = | |
| dom::KeyboardEvent::KeyCodeToKeyLocation(key_code); | |
| dom::KeyboardEventInit keyboard_event; | |
| UpdateEventInit(input_event, &keyboard_event); | |
| UpdateEventModifierInit(input_event, &keyboard_event); | |
| keyboard_event.set_location(location); | |
| keyboard_event.set_repeat(input_event->is_repeat()); | |
| keyboard_event.set_char_code(key_code); | |
| keyboard_event.set_key_code(key_code); | |
| keypress_generator_filter_.HandleKeyboardEvent(type, keyboard_event); | |
| int32_t key_code_in_int32 = static_cast<int32_t>(key_code); | |
| overlay_info::OverlayInfoRegistry::Register("keydown", key_code_in_int32); | |
| } | |
| void InputDeviceManagerDesktop::HandlePointerEvent( | |
| base::Token type, const system_window::InputEvent* input_event) { | |
| dom::PointerEventInit pointer_event; | |
| UpdateEventInit(input_event, &pointer_event); | |
| UpdateMouseEventInit(input_event, &pointer_event); | |
| switch (input_event->type()) { | |
| case system_window::InputEvent::kTouchpadDown: | |
| case system_window::InputEvent::kTouchpadUp: | |
| case system_window::InputEvent::kTouchpadMove: | |
| pointer_event.set_pointer_type("touchpad"); | |
| break; | |
| case system_window::InputEvent::kTouchscreenDown: | |
| case system_window::InputEvent::kTouchscreenUp: | |
| case system_window::InputEvent::kTouchscreenMove: | |
| pointer_event.set_pointer_type("touch"); | |
| break; | |
| case system_window::InputEvent::kKeyDown: | |
| case system_window::InputEvent::kKeyUp: | |
| case system_window::InputEvent::kKeyMove: | |
| case system_window::InputEvent::kInput: | |
| case system_window::InputEvent::kPointerDown: | |
| case system_window::InputEvent::kPointerMove: | |
| case system_window::InputEvent::kPointerUp: | |
| case system_window::InputEvent::kWheel: | |
| pointer_event.set_pointer_type("mouse"); | |
| break; | |
| } | |
| pointer_event.set_pointer_id(input_event->device_id()); | |
| pointer_event.set_width(value_or(input_event->size().x(), 0.0f)); | |
| pointer_event.set_height(value_or(input_event->size().y(), 0.0f)); | |
| pointer_event.set_pressure(value_or(input_event->pressure(), | |
| input_event->modifiers() ? 0.5f : 0.0f)); | |
| pointer_event.set_tilt_x( | |
| value_or(static_cast<float>(input_event->tilt().x()), 0.0f)); | |
| pointer_event.set_tilt_y( | |
| value_or(static_cast<float>(input_event->tilt().y()), 0.0f)); | |
| pointer_event.set_is_primary(true); | |
| pointer_event_callback_.Run(type, pointer_event); | |
| } | |
| void InputDeviceManagerDesktop::HandleWheelEvent( | |
| const system_window::InputEvent* input_event) { | |
| base::Token type = base::Tokens::wheel(); | |
| dom::WheelEventInit wheel_event; | |
| UpdateEventInit(input_event, &wheel_event); | |
| UpdateMouseEventInit(input_event, &wheel_event); | |
| wheel_event.set_delta_x(input_event->delta().x()); | |
| wheel_event.set_delta_y(input_event->delta().y()); | |
| wheel_event.set_delta_z(0); | |
| wheel_event.set_delta_mode(dom::WheelEvent::kDomDeltaLine); | |
| wheel_event_callback_.Run(type, wheel_event); | |
| } | |
| void InputDeviceManagerDesktop::HandleInputEvent( | |
| const system_window::InputEvent* event) { | |
| // Note: we currently treat all dom::InputEvents as input (never beforeinput). | |
| base::Token type = base::Tokens::input(); | |
| dom::InputEventInit input_event; | |
| UpdateEventInit(event, &input_event); | |
| input_event.set_data(event->input_text()); | |
| input_event.set_is_composing(event->is_composing()); | |
| input_event_callback_.Run(type, input_event); | |
| } | |
| void InputDeviceManagerDesktop::HandleSystemWindowInputEvent( | |
| const base::Event* event) { | |
| // The user has pressed a key on the keyboard. | |
| const system_window::InputEvent* input_event = | |
| base::polymorphic_downcast<const system_window::InputEvent*>(event); | |
| SB_DCHECK(input_event); | |
| int key_code = input_event->key_code(); | |
| switch (input_event->type()) { | |
| case system_window::InputEvent::kKeyDown: | |
| HandleKeyboardEvent(true, input_event, key_code); | |
| break; | |
| case system_window::InputEvent::kKeyUp: | |
| HandleKeyboardEvent(false, input_event, key_code); | |
| break; | |
| case system_window::InputEvent::kPointerDown: | |
| case system_window::InputEvent::kPointerUp: { | |
| if ((kSbKeyBrowserBack == key_code) || | |
| (kSbKeyBrowserForward == key_code) || (kSbKeyMouse2 == key_code)) { | |
| // For consistency with behavior on current browsers, the 'Forward' and | |
| // 'Back' mouse buttons are reported as keypress input for the 'Forward' | |
| // and 'Back' navigation keys, not as Pointer Events for the X1 and X2 | |
| // buttons. | |
| if (kSbKeyMouse2 == key_code) { | |
| // Temporarily Report middle button presses as the enter key. | |
| key_code = kSbKeyReturn; | |
| } | |
| HandleKeyboardEvent( | |
| input_event->type() == system_window::InputEvent::kPointerDown, | |
| input_event, key_code); | |
| } else { | |
| base::Token type = | |
| input_event->type() == system_window::InputEvent::kPointerDown | |
| ? base::Tokens::pointerdown() | |
| : base::Tokens::pointerup(); | |
| HandlePointerEvent(type, input_event); | |
| } | |
| break; | |
| } | |
| case system_window::InputEvent::kPointerMove: | |
| case system_window::InputEvent::kTouchpadMove: | |
| case system_window::InputEvent::kTouchscreenMove: { | |
| HandlePointerEvent(base::Tokens::pointermove(), input_event); | |
| break; | |
| } | |
| case system_window::InputEvent::kTouchpadDown: | |
| case system_window::InputEvent::kTouchscreenDown: | |
| HandlePointerEvent(base::Tokens::pointerdown(), input_event); | |
| break; | |
| case system_window::InputEvent::kTouchpadUp: | |
| case system_window::InputEvent::kTouchscreenUp: | |
| HandlePointerEvent(base::Tokens::pointerup(), input_event); | |
| break; | |
| case system_window::InputEvent::kWheel: | |
| HandleWheelEvent(input_event); | |
| break; | |
| case system_window::InputEvent::kInput: | |
| HandleInputEvent(input_event); | |
| break; | |
| case system_window::InputEvent::kKeyMove: | |
| break; | |
| } | |
| InputPollerImpl* input_poller_impl = | |
| static_cast<InputPollerImpl*>(input_poller_.get()); | |
| input_poller_impl->UpdateInputEvent(input_event); | |
| } | |
| } // namespace input | |
| } // namespace cobalt |