From 6e8fa43a914a1adceb86d810738736ed8e7963b3 Mon Sep 17 00:00:00 2001 From: Joe Downing Date: Sat, 4 Dec 2021 22:14:34 +0000 Subject: [PATCH] Add Input injection methods to Chromoting Mojo interface This CL removes the input injection messages from our Chromoting IPC messages file and implements them via our new Mojo interface. The majority of this CL is the code needed to serialize and deserialize the protobuf messages involved. One quirk that I encountered with this is that you can't have optional primitive fields in a Mojo struct. I solved this by boxing the primitive in a struct which I could declare as nullable in the message struct. If there is a better or pre-existing mechanism, I'm happy to switch to it. Bug: b:178114059 Change-Id: Ib1e200f0b49483ef448757ca8b32d4781bc70718 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3287408 Commit-Queue: Joe Downing Reviewed-by: Daniel Cheng Reviewed-by: Yuwei Huang Cr-Commit-Position: refs/heads/main@{#948346} --- remoting/host/chromoting_messages.h | 20 - remoting/host/desktop_session_agent.cc | 44 +-- remoting/host/desktop_session_agent.h | 8 +- remoting/host/desktop_session_proxy.cc | 36 +- remoting/host/mojom/BUILD.gn | 51 ++- remoting/host/mojom/desktop_session.mojom | 125 ++++++ remoting/host/mojom/remoting_mojom_traits.cc | 174 +++++++++ remoting/host/mojom/remoting_mojom_traits.h | 383 ++++++++++++++++++- remoting/host/mojom/wrapped_primitives.mojom | 24 ++ remoting/proto/event.proto | 1 + 10 files changed, 758 insertions(+), 108 deletions(-) create mode 100644 remoting/host/mojom/wrapped_primitives.mojom diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h index 4e123e65019de..7035f77bbb622 100644 --- a/remoting/host/chromoting_messages.h +++ b/remoting/host/chromoting_messages.h @@ -227,26 +227,6 @@ IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_CaptureFrame) IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_SelectSource, int /* desktop_display_id */) -// Carries a keyboard event from the client to the desktop session agent. -// |serialized_event| is a serialized protocol::KeyEvent. -IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_InjectKeyEvent, - std::string /* serialized_event */) - -// Carries a keyboard event from the client to the desktop session agent. -// |serialized_event| is a serialized protocol::TextEvent. -IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_InjectTextEvent, - std::string /* serialized_event */) - -// Carries a mouse event from the client to the desktop session agent. -// |serialized_event| is a serialized protocol::MouseEvent. -IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_InjectMouseEvent, - std::string /* serialized_event */) - -// Carries a touch event from the client to the desktop session agent. -// |serialized_event| is a serialized protocol::TouchEvent. -IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_InjectTouchEvent, - std::string /* serialized_event */) - // Changes the screen resolution in the desktop session. IPC_MESSAGE_CONTROL(ChromotingNetworkDesktopMsg_SetScreenResolution, remoting::ScreenResolution /* resolution */) diff --git a/remoting/host/desktop_session_agent.cc b/remoting/host/desktop_session_agent.cc index 7b33e21922800..c5f58d2bf39c4 100644 --- a/remoting/host/desktop_session_agent.cc +++ b/remoting/host/desktop_session_agent.cc @@ -243,14 +243,6 @@ bool DesktopSessionAgent::OnMessageReceived(const IPC::Message& message) { OnCaptureFrame) IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SelectSource, OnSelectSource) - IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectKeyEvent, - OnInjectKeyEvent) - IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectTextEvent, - OnInjectTextEvent) - IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectMouseEvent, - OnInjectMouseEvent) - IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_InjectTouchEvent, - OnInjectTouchEvent) IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_ExecuteActionRequest, OnExecuteActionRequestEvent) IPC_MESSAGE_HANDLER(ChromotingNetworkDesktopMsg_SetScreenResolution, @@ -647,16 +639,9 @@ void DesktopSessionAgent::InjectClipboardEvent( input_injector_->InjectClipboardEvent(event); } -void DesktopSessionAgent::OnInjectKeyEvent( - const std::string& serialized_event) { +void DesktopSessionAgent::InjectKeyEvent(const protocol::KeyEvent& event) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - protocol::KeyEvent event; - if (!event.ParseFromString(serialized_event)) { - LOG(ERROR) << "Failed to parse protocol::KeyEvent."; - return; - } - // InputStub implementations must verify events themselves, so we need only // basic verification here. This matches HostEventDispatcher. if (!event.has_usb_keycode() || !event.has_pressed()) { @@ -667,16 +652,9 @@ void DesktopSessionAgent::OnInjectKeyEvent( remote_input_filter_->InjectKeyEvent(event); } -void DesktopSessionAgent::OnInjectTextEvent( - const std::string& serialized_event) { +void DesktopSessionAgent::InjectTextEvent(const protocol::TextEvent& event) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - protocol::TextEvent event; - if (!event.ParseFromString(serialized_event)) { - LOG(ERROR) << "Failed to parse protocol::TextEvent."; - return; - } - // InputStub implementations must verify events themselves, so we need only // basic verification here. This matches HostEventDispatcher. if (!event.has_text()) { @@ -687,16 +665,9 @@ void DesktopSessionAgent::OnInjectTextEvent( remote_input_filter_->InjectTextEvent(event); } -void DesktopSessionAgent::OnInjectMouseEvent( - const std::string& serialized_event) { +void DesktopSessionAgent::InjectMouseEvent(const protocol::MouseEvent& event) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - protocol::MouseEvent event; - if (!event.ParseFromString(serialized_event)) { - LOG(ERROR) << "Failed to parse protocol::MouseEvent."; - return; - } - if (video_capturer_) video_capturer_->SetComposeEnabled(event.has_delta_x() || event.has_delta_y()); @@ -706,16 +677,9 @@ void DesktopSessionAgent::OnInjectMouseEvent( remote_input_filter_->InjectMouseEvent(event); } -void DesktopSessionAgent::OnInjectTouchEvent( - const std::string& serialized_event) { +void DesktopSessionAgent::InjectTouchEvent(const protocol::TouchEvent& event) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - protocol::TouchEvent event; - if (!event.ParseFromString(serialized_event)) { - LOG(ERROR) << "Failed to parse protocol::TouchEvent."; - return; - } - remote_input_filter_->InjectTouchEvent(event); } diff --git a/remoting/host/desktop_session_agent.h b/remoting/host/desktop_session_agent.h index 82b8c9c6f18f7..bbf7c3537eb7d 100644 --- a/remoting/host/desktop_session_agent.h +++ b/remoting/host/desktop_session_agent.h @@ -133,6 +133,10 @@ class DesktopSessionAgent // mojom::DesktopSessionControl implementation. void InjectClipboardEvent(const protocol::ClipboardEvent& event) override; + void InjectKeyEvent(const protocol::KeyEvent& event) override; + void InjectMouseEvent(const protocol::MouseEvent& event) override; + void InjectTextEvent(const protocol::TextEvent& event) override; + void InjectTouchEvent(const protocol::TouchEvent& event) override; void SetUpUrlForwarder() override; // Creates desktop integration components and a connected IPC channel to be @@ -169,10 +173,6 @@ class DesktopSessionAgent void OnSelectSource(int id); // Handles event executor requests from the client. - void OnInjectKeyEvent(const std::string& serialized_event); - void OnInjectTextEvent(const std::string& serialized_event); - void OnInjectMouseEvent(const std::string& serialized_event); - void OnInjectTouchEvent(const std::string& serialized_event); void OnExecuteActionRequestEvent(const protocol::ActionRequest& request); // Handles keyboard layout changes. diff --git a/remoting/host/desktop_session_proxy.cc b/remoting/host/desktop_session_proxy.cc index 344cf05dba835..814a8ce79dfb7 100644 --- a/remoting/host/desktop_session_proxy.cc +++ b/remoting/host/desktop_session_proxy.cc @@ -418,53 +418,33 @@ void DesktopSessionProxy::InjectClipboardEvent( void DesktopSessionProxy::InjectKeyEvent(const protocol::KeyEvent& event) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - std::string serialized_event; - if (!event.SerializeToString(&serialized_event)) { - LOG(ERROR) << "Failed to serialize protocol::KeyEvent."; - return; + if (desktop_session_control_) { + desktop_session_control_->InjectKeyEvent(event); } - - SendToDesktop( - new ChromotingNetworkDesktopMsg_InjectKeyEvent(serialized_event)); } void DesktopSessionProxy::InjectTextEvent(const protocol::TextEvent& event) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - std::string serialized_event; - if (!event.SerializeToString(&serialized_event)) { - LOG(ERROR) << "Failed to serialize protocol::TextEvent."; - return; + if (desktop_session_control_) { + desktop_session_control_->InjectTextEvent(event); } - - SendToDesktop( - new ChromotingNetworkDesktopMsg_InjectTextEvent(serialized_event)); } void DesktopSessionProxy::InjectMouseEvent(const protocol::MouseEvent& event) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - std::string serialized_event; - if (!event.SerializeToString(&serialized_event)) { - LOG(ERROR) << "Failed to serialize protocol::MouseEvent."; - return; + if (desktop_session_control_) { + desktop_session_control_->InjectMouseEvent(event); } - - SendToDesktop( - new ChromotingNetworkDesktopMsg_InjectMouseEvent(serialized_event)); } void DesktopSessionProxy::InjectTouchEvent(const protocol::TouchEvent& event) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - std::string serialized_event; - if (!event.SerializeToString(&serialized_event)) { - LOG(ERROR) << "Failed to serialize protocol::TouchEvent."; - return; + if (desktop_session_control_) { + desktop_session_control_->InjectTouchEvent(event); } - - SendToDesktop( - new ChromotingNetworkDesktopMsg_InjectTouchEvent(serialized_event)); } void DesktopSessionProxy::StartInputInjector( diff --git a/remoting/host/mojom/BUILD.gn b/remoting/host/mojom/BUILD.gn index d95f05325f8d6..23781de937f77 100644 --- a/remoting/host/mojom/BUILD.gn +++ b/remoting/host/mojom/BUILD.gn @@ -14,10 +14,12 @@ mojom("mojom") { "remoting_host.mojom", "testing.mojom", "webauthn_proxy.mojom", + "wrapped_primitives.mojom", ] deps = [ "//mojo/public/mojom/base", + "//ui/gfx/geometry/mojom", "//url/mojom:url_mojom_gurl", ] @@ -28,23 +30,42 @@ mojom("mojom") { mojom = "remoting.mojom.ClipboardEvent" cpp = "::remoting::protocol::ClipboardEvent" }, + { + mojom = "remoting.mojom.KeyEvent" + cpp = "::remoting::protocol::KeyEvent" + }, + { + mojom = "remoting.mojom.MouseButton" + cpp = "::remoting::protocol::MouseEvent::MouseButton" + }, + { + mojom = "remoting.mojom.MouseEvent" + cpp = "::remoting::protocol::MouseEvent" + }, + { + mojom = "remoting.mojom.TextEvent" + cpp = "::remoting::protocol::TextEvent" + }, + { + mojom = "remoting.mojom.TouchEvent" + cpp = "::remoting::protocol::TouchEvent" + }, + { + mojom = "remoting.mojom.TouchEventPoint" + cpp = "::remoting::protocol::TouchEventPoint" + }, + { + mojom = "remoting.mojom.TouchEventType" + cpp = "::remoting::protocol::TouchEvent::TouchEventType" + }, ] traits_headers = [ "remoting_mojom_traits.h" ] - traits_public_deps = [ ":mojom_traits" ] + traits_sources = [ "remoting_mojom_traits.cc" ] + traits_public_deps = [ + "//mojo/public/cpp/base:shared_typemap_traits", + "//mojo/public/cpp/bindings", + "//remoting/proto", + ] }, ] } - -source_set("mojom_traits") { - sources = [ - "remoting_mojom_traits.cc", - "remoting_mojom_traits.h", - ] - - public_deps = [ - ":mojom_shared_cpp_sources", - "//mojo/public/cpp/base:shared_typemap_traits", - "//mojo/public/cpp/bindings", - "//remoting/proto", - ] -} diff --git a/remoting/host/mojom/desktop_session.mojom b/remoting/host/mojom/desktop_session.mojom index 29c4cd4ceb807..bcee3fac07857 100644 --- a/remoting/host/mojom/desktop_session.mojom +++ b/remoting/host/mojom/desktop_session.mojom @@ -5,6 +5,8 @@ module remoting.mojom; import "mojo/public/mojom/base/byte_string.mojom"; +import "remoting/host/mojom/wrapped_primitives.mojom"; +import "ui/gfx/geometry/mojom/geometry.mojom"; // Contains clipboard event data. // This struct mirrors the remoting::protocol::ClipboardEvents protobuf struct. @@ -17,6 +19,117 @@ struct ClipboardEvent { mojo_base.mojom.ByteString data; }; +// Contains the data needed to inject a keyboard key event. +// This struct mirrors the remoting::protocol::KeyEvent protobuf struct. +struct KeyEvent { + // True for key press events, and false for key release. + bool pressed; + + // The USB key code. + // The upper 16-bits are the USB Page (0x07 for key events). + // The lower 16-bits are the USB Usage ID (which identifies the actual key). + uint32 usb_keycode; + + // Legacy keyboard lock states. Prefer the discrete entries below. + // Flags defined in remoting::protocol::KeyEvent::LockStates. + uint32 lock_states; + + // Keyboard lock states. The field should be specified only if the state can + // be reliably determined by the client. E.g., OS X does not have num lock, so + // only caps_lock should be provided by a client running on OS X. + Bool? caps_lock_state; + Bool? num_lock_state; +}; + +// This enum mirrors the remoting::protocol::MouseEvent::MouseButton enum. +enum MouseButton { + kUndefined = 0, + kLeft = 1, + kMiddle = 2, + kRight = 3, + kBack = 4, + kForward = 5, +}; + +// Contains the data needed to inject a mouse event. +// This struct mirrors the remoting::protocol::MouseEvent protobuf struct. +struct MouseEvent { + // Mouse absolute position information. When using WebRTC-based protocol the + // coordinates are in DIPs. Otherwise they are in host's physical pixels. In + // both coordinates systems, the top-left monitor on the system always starts + // from (0, 0). + Int32? x; + Int32? y; + + // Mouse button event. + MouseButton button; + // |button_down| is not set when |button| is kUndefined. + Bool? button_down; + + // Mouse wheel information. + // These values encode the number of pixels and 'ticks' of movement that + // would result from the wheel event on the client system. + Float? wheel_delta_x; + Float? wheel_delta_y; + Float? wheel_ticks_x; + Float? wheel_ticks_y; + + // Mouse movement information. Provided only when mouse lock is engaged. + Int32? delta_x; + Int32? delta_y; +}; + +// Text input event for input method different from physical keyboards, +// including software keyboard, gesture typing, voice input, etc. +// This struct mirrors the remoting::protocol::TextEvent protobuf struct. +struct TextEvent { + // Unicode sequence for the event in UTF-8. + string text; +}; + +// Describes the attributes for a single touch point, used in TouchEvent to +// describe a touch gesture. +// This struct mirrors the remoting::protocol::TouchEventPoint protobuf struct. +struct TouchEventPoint { + // The ID for the touch point. + uint32 id; + + // The absolute position of the touch point. These values on-the-wire are host + // physical pixel coordinates: the top-left monitor on the system always + // starts from (0, 0). + gfx.mojom.PointF position; + + // The size of the touch point, used to aid hit-testing. + // Scaled to match the size on host. + gfx.mojom.PointF radius; + + // Angle in degrees from the y-axis of the touch point. + float angle; + + // The pressure of the touch point. + // The value should be in [0.0, 1.0]. + float pressure; +}; + +// This enum mirrors the remoting::protocol::TouchEvent::TouchEventType enum. +enum TouchEventType { + kUndefined = 0, + kStart = 1, + kMove = 2, + kEnd = 3, + kCancel = 4, +}; + +// Describes a touch gesture event (start, stop, move) along with the set of +// touch points to include in the gesture. +struct TouchEvent { + // The event type. + TouchEventType event_type; + + // Only the changed touch points are added to this field. + array touch_points; +}; + // The state of the URL forwarder setup. enum UrlForwarderState { kUnknown = 0, @@ -67,6 +180,18 @@ interface DesktopSessionControl { // Used to inject clipboard events received from the client. InjectClipboardEvent(ClipboardEvent event); + // Used to inject keyboard events received from the client. + InjectKeyEvent(KeyEvent event); + + // Used to inject mouse events received from the client. + InjectMouseEvent(MouseEvent event); + + // Used to inject text events received from the client. + InjectTextEvent(TextEvent event); + + // Used to inject touch events received from the client. + InjectTouchEvent(TouchEvent event); + // Used to set up the URL forwarder as the default browser. State changes // will be reported via the // DesktopSessionEventHandler::OnUrlForwarderStateChange() method. diff --git a/remoting/host/mojom/remoting_mojom_traits.cc b/remoting/host/mojom/remoting_mojom_traits.cc index ec544005d3b75..bd1df610dc486 100644 --- a/remoting/host/mojom/remoting_mojom_traits.cc +++ b/remoting/host/mojom/remoting_mojom_traits.cc @@ -24,4 +24,178 @@ bool mojo::StructTraits:: + Read(remoting::mojom::KeyEventDataView data_view, + ::remoting::protocol::KeyEvent* out_event) { + out_event->set_pressed(data_view.pressed()); + out_event->set_usb_keycode(data_view.usb_keycode()); + out_event->set_lock_states(data_view.lock_states()); + + absl::optional caps_lock_state; + if (!data_view.ReadCapsLockState(&caps_lock_state)) { + return false; + } + if (caps_lock_state.has_value()) { + out_event->set_caps_lock_state(caps_lock_state.value()); + } + + absl::optional num_lock_state; + if (!data_view.ReadNumLockState(&num_lock_state)) { + return false; + } + if (num_lock_state.has_value()) { + out_event->set_num_lock_state(num_lock_state.value()); + } + + return true; +} + +// static +bool mojo::StructTraits:: + Read(remoting::mojom::MouseEventDataView data_view, + ::remoting::protocol::MouseEvent* out_event) { + absl::optional x; + if (!data_view.ReadX(&x)) { + return false; + } + if (x.has_value()) { + out_event->set_x(x.value()); + } + + absl::optional y; + if (!data_view.ReadY(&y)) { + return false; + } + if (y.has_value()) { + out_event->set_y(y.value()); + } + + if (data_view.button() != remoting::mojom::MouseButton::kUndefined) { + ::remoting::protocol::MouseEvent::MouseButton mouse_button; + if (!EnumTraits:: + FromMojom(data_view.button(), &mouse_button)) { + return false; + } + out_event->set_button(mouse_button); + } + + absl::optional button_down; + if (!data_view.ReadButtonDown(&button_down)) { + return false; + } + if (button_down.has_value()) { + out_event->set_button_down(button_down.value()); + } + + absl::optional wheel_delta_x; + if (!data_view.ReadWheelDeltaX(&wheel_delta_x)) { + return false; + } + if (wheel_delta_x.has_value()) { + out_event->set_wheel_delta_x(wheel_delta_x.value()); + } + + absl::optional wheel_delta_y; + if (!data_view.ReadWheelDeltaY(&wheel_delta_y)) { + return false; + } + if (wheel_delta_y.has_value()) { + out_event->set_wheel_delta_y(wheel_delta_y.value()); + } + + absl::optional wheel_ticks_x; + if (!data_view.ReadWheelTicksX(&wheel_ticks_x)) { + return false; + } + if (wheel_ticks_x.has_value()) { + out_event->set_wheel_ticks_x(wheel_ticks_x.value()); + } + + absl::optional wheel_ticks_y; + if (!data_view.ReadWheelTicksY(&wheel_ticks_y)) { + return false; + } + if (wheel_ticks_y.has_value()) { + out_event->set_wheel_ticks_y(wheel_ticks_y.value()); + } + + absl::optional delta_x; + if (!data_view.ReadDeltaX(&delta_x)) { + return false; + } + if (delta_x.has_value()) { + out_event->set_delta_x(delta_x.value()); + } + + absl::optional delta_y; + if (!data_view.ReadDeltaY(&delta_y)) { + return false; + } + if (delta_y.has_value()) { + out_event->set_delta_y(delta_y.value()); + } + + return true; +} + +// static +bool mojo::StructTraits:: + Read(remoting::mojom::TextEventDataView data_view, + ::remoting::protocol::TextEvent* out_event) { + std::string text; + if (!data_view.ReadText(&text)) { + return false; + } + out_event->set_text(std::move(text)); + return true; +} + +// static +bool mojo::StructTraits:: + Read(remoting::mojom::TouchEventPointDataView data_view, + ::remoting::protocol::TouchEventPoint* out_event) { + out_event->set_id(data_view.id()); + gfx::PointF position; + if (!data_view.ReadPosition(&position)) { + return false; + } + out_event->set_x(position.x()); + out_event->set_y(position.y()); + gfx::PointF radius; + if (!data_view.ReadRadius(&radius)) { + return false; + } + out_event->set_radius_x(radius.x()); + out_event->set_radius_y(radius.y()); + out_event->set_angle(data_view.angle()); + out_event->set_pressure(data_view.pressure()); + return true; +} + +// static +bool mojo::StructTraits:: + Read(remoting::mojom::TouchEventDataView data_view, + ::remoting::protocol::TouchEvent* out_event) { + ::remoting::protocol::TouchEvent::TouchEventType touch_event_type; + if (!EnumTraits:: + FromMojom(data_view.event_type(), &touch_event_type)) { + return false; + } + out_event->set_event_type(touch_event_type); + + if (!data_view.ReadTouchPoints(out_event->mutable_touch_points())) { + return false; + } + + return true; +} + } // namespace mojo diff --git a/remoting/host/mojom/remoting_mojom_traits.h b/remoting/host/mojom/remoting_mojom_traits.h index d6d41cffe1443..a9d232d8b35a2 100644 --- a/remoting/host/mojom/remoting_mojom_traits.h +++ b/remoting/host/mojom/remoting_mojom_traits.h @@ -7,15 +7,108 @@ #include #include -#include +#include "base/numerics/safe_conversions.h" #include "mojo/public/cpp/base/byte_string_mojom_traits.h" +#include "mojo/public/cpp/bindings/array_traits.h" +#include "mojo/public/cpp/bindings/enum_traits.h" #include "mojo/public/cpp/bindings/struct_traits.h" #include "remoting/host/mojom/desktop_session.mojom-shared.h" +#include "remoting/host/mojom/wrapped_primitives.mojom-shared.h" #include "remoting/proto/event.pb.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/gfx/geometry/mojom/geometry_mojom_traits.h" namespace mojo { +template <> +class mojo::StructTraits { + public: + static bool value(bool value) { return value; } + + static bool Read(remoting::mojom::BoolDataView data_view, bool* out_value) { + *out_value = data_view.value(); + return true; + } +}; + +template <> +class mojo::StructTraits { + public: + static float value(float value) { return value; } + + static bool Read(remoting::mojom::FloatDataView data_view, float* out_value) { + *out_value = data_view.value(); + return true; + } +}; + +template <> +class mojo::StructTraits { + public: + static int32_t value(int32_t value) { return value; } + + static bool Read(remoting::mojom::Int32DataView data_view, + int32_t* out_value) { + *out_value = data_view.value(); + return true; + } +}; + +template <> +struct EnumTraits { + static remoting::mojom::MouseButton ToMojom( + ::remoting::protocol::MouseEvent::MouseButton input) { + switch (input) { + case ::remoting::protocol::MouseEvent::BUTTON_UNDEFINED: + return remoting::mojom::MouseButton::kUndefined; + case ::remoting::protocol::MouseEvent::BUTTON_LEFT: + return remoting::mojom::MouseButton::kLeft; + case ::remoting::protocol::MouseEvent::BUTTON_MIDDLE: + return remoting::mojom::MouseButton::kMiddle; + case ::remoting::protocol::MouseEvent::BUTTON_RIGHT: + return remoting::mojom::MouseButton::kRight; + case ::remoting::protocol::MouseEvent::BUTTON_BACK: + return remoting::mojom::MouseButton::kBack; + case ::remoting::protocol::MouseEvent::BUTTON_FORWARD: + return remoting::mojom::MouseButton::kForward; + case ::remoting::protocol::MouseEvent::BUTTON_MAX: + break; + } + + NOTREACHED(); + return remoting::mojom::MouseButton::kUndefined; + } + + static bool FromMojom(remoting::mojom::MouseButton input, + ::remoting::protocol::MouseEvent::MouseButton* out) { + switch (input) { + case remoting::mojom::MouseButton::kUndefined: + *out = ::remoting::protocol::MouseEvent::BUTTON_UNDEFINED; + return true; + case remoting::mojom::MouseButton::kLeft: + *out = ::remoting::protocol::MouseEvent::BUTTON_LEFT; + return true; + case remoting::mojom::MouseButton::kMiddle: + *out = ::remoting::protocol::MouseEvent::BUTTON_MIDDLE; + return true; + case remoting::mojom::MouseButton::kRight: + *out = ::remoting::protocol::MouseEvent::BUTTON_RIGHT; + return true; + case remoting::mojom::MouseButton::kBack: + *out = ::remoting::protocol::MouseEvent::BUTTON_BACK; + return true; + case remoting::mojom::MouseButton::kForward: + *out = ::remoting::protocol::MouseEvent::BUTTON_FORWARD; + return true; + } + + NOTREACHED(); + return false; + } +}; + template <> class mojo::StructTraits { @@ -34,6 +127,294 @@ class mojo::StructTraits +class mojo::StructTraits { + public: + static bool pressed(const ::remoting::protocol::KeyEvent& event) { + return event.pressed(); + } + + static uint32_t usb_keycode(const ::remoting::protocol::KeyEvent& event) { + return event.usb_keycode(); + } + + static uint32_t lock_states(const ::remoting::protocol::KeyEvent& event) { + return event.lock_states(); + } + + static absl::optional caps_lock_state( + const ::remoting::protocol::KeyEvent& event) { + if (event.has_caps_lock_state()) { + return event.caps_lock_state(); + } + return absl::nullopt; + } + + static absl::optional num_lock_state( + const ::remoting::protocol::KeyEvent& event) { + if (event.has_num_lock_state()) { + return event.num_lock_state(); + } + return absl::nullopt; + } + + static bool Read(remoting::mojom::KeyEventDataView data_view, + ::remoting::protocol::KeyEvent* out_event); +}; + +template <> +class mojo::StructTraits { + public: + static absl::optional x( + const ::remoting::protocol::MouseEvent& event) { + if (event.has_x()) { + return event.x(); + } + return absl::nullopt; + } + + static absl::optional y( + const ::remoting::protocol::MouseEvent& event) { + if (event.has_y()) { + return event.y(); + } + return absl::nullopt; + } + + static ::remoting::protocol::MouseEvent::MouseButton button( + const ::remoting::protocol::MouseEvent& event) { + if (event.has_button()) { + return event.button(); + } + return ::remoting::protocol::MouseEvent::BUTTON_UNDEFINED; + } + + static absl::optional button_down( + const ::remoting::protocol::MouseEvent& event) { + if (event.has_button_down()) { + DCHECK(event.has_button()); + return event.button_down(); + } + return absl::nullopt; + } + + static absl::optional wheel_delta_x( + const ::remoting::protocol::MouseEvent& event) { + if (event.has_wheel_delta_x()) { + return event.wheel_delta_x(); + } + return absl::nullopt; + } + + static absl::optional wheel_delta_y( + const ::remoting::protocol::MouseEvent& event) { + if (event.has_wheel_delta_y()) { + return event.wheel_delta_y(); + } + return absl::nullopt; + } + + static absl::optional wheel_ticks_x( + const ::remoting::protocol::MouseEvent& event) { + if (event.wheel_ticks_x()) { + return event.wheel_ticks_x(); + } + return absl::nullopt; + } + + static absl::optional wheel_ticks_y( + const ::remoting::protocol::MouseEvent& event) { + if (event.wheel_ticks_y()) { + return event.wheel_ticks_y(); + } + return absl::nullopt; + } + + static absl::optional delta_x( + const ::remoting::protocol::MouseEvent& event) { + if (event.has_delta_x()) { + return event.delta_x(); + } + return absl::nullopt; + } + + static absl::optional delta_y( + const ::remoting::protocol::MouseEvent& event) { + if (event.has_delta_y()) { + return event.delta_y(); + } + return absl::nullopt; + } + + static bool Read(remoting::mojom::MouseEventDataView data_view, + ::remoting::protocol::MouseEvent* out_event); +}; + +template <> +class mojo::StructTraits { + public: + static const std::string& text(const ::remoting::protocol::TextEvent& event) { + return event.text(); + } + + static bool Read(remoting::mojom::TextEventDataView data_view, + ::remoting::protocol::TextEvent* out_event); +}; + +template <> +class mojo::StructTraits { + public: + static uint32_t id(const ::remoting::protocol::TouchEventPoint& event) { + return event.id(); + } + + static gfx::PointF position( + const ::remoting::protocol::TouchEventPoint& event) { + return {event.x(), event.y()}; + } + + static gfx::PointF radius( + const ::remoting::protocol::TouchEventPoint& event) { + return {event.radius_x(), event.radius_y()}; + } + + static float angle(const ::remoting::protocol::TouchEventPoint& event) { + return event.angle(); + } + + static float pressure(const ::remoting::protocol::TouchEventPoint& event) { + return event.pressure(); + } + + static bool Read(remoting::mojom::TouchEventPointDataView data_view, + ::remoting::protocol::TouchEventPoint* out_event); +}; + +// TODO(joedow): Move this to //mojo/public/cpp/bindings. +template +struct ArrayTraits<::google::protobuf::RepeatedPtrField> { + using Element = T; + + static bool IsNull(const ::google::protobuf::RepeatedPtrField& input) { + // Always convert RepeatedPtrField to a non-null mojom array. + return false; + } + + static T* GetData(::google::protobuf::RepeatedPtrField& input) { + return input.data(); + } + + static const T* GetData( + const ::google::protobuf::RepeatedPtrField& input) { + return input.data(); + } + + static T& GetAt(::google::protobuf::RepeatedPtrField& input, + size_t index) { + return input.at(index); + } + + static const T& GetAt(const ::google::protobuf::RepeatedPtrField& input, + size_t index) { + return input.at(index); + } + + static size_t GetSize(const ::google::protobuf::RepeatedPtrField& input) { + return input.size(); + } + + static bool Resize(::google::protobuf::RepeatedPtrField& input, + size_t new_size) { + if (!base::IsValueInRangeForNumericType(new_size)) { + return false; + } + + // We call Reserve() to set the capacity and then add elements to increase + // the container size to the requested value. We can't rely on Reserve() + // alone as that will resize the container but size() will still report the + // previous number of elements which will cause deserialization failures. + // Unfortunately there isn't an AddRange() or similar function available so + // we need to add elements one at a time in a loop. + int requested_size = base::checked_cast(new_size); + input.Reserve(requested_size); + while (input.size() < requested_size) { + input.Add(); + } + + return true; + } +}; + +template <> +struct EnumTraits { + static remoting::mojom::TouchEventType ToMojom( + ::remoting::protocol::TouchEvent::TouchEventType input) { + switch (input) { + case ::remoting::protocol::TouchEvent::TOUCH_POINT_UNDEFINED: + return remoting::mojom::TouchEventType::kUndefined; + case ::remoting::protocol::TouchEvent::TOUCH_POINT_START: + return remoting::mojom::TouchEventType::kStart; + case ::remoting::protocol::TouchEvent::TOUCH_POINT_MOVE: + return remoting::mojom::TouchEventType::kMove; + case ::remoting::protocol::TouchEvent::TOUCH_POINT_END: + return remoting::mojom::TouchEventType::kEnd; + case ::remoting::protocol::TouchEvent::TOUCH_POINT_CANCEL: + return remoting::mojom::TouchEventType::kCancel; + } + + NOTREACHED(); + return remoting::mojom::TouchEventType::kUndefined; + } + + static bool FromMojom(remoting::mojom::TouchEventType input, + ::remoting::protocol::TouchEvent::TouchEventType* out) { + switch (input) { + case remoting::mojom::TouchEventType::kUndefined: + *out = ::remoting::protocol::TouchEvent::TOUCH_POINT_UNDEFINED; + return true; + case remoting::mojom::TouchEventType::kStart: + *out = ::remoting::protocol::TouchEvent::TOUCH_POINT_START; + return true; + case remoting::mojom::TouchEventType::kMove: + *out = ::remoting::protocol::TouchEvent::TOUCH_POINT_MOVE; + return true; + case remoting::mojom::TouchEventType::kEnd: + *out = ::remoting::protocol::TouchEvent::TOUCH_POINT_END; + return true; + case remoting::mojom::TouchEventType::kCancel: + *out = ::remoting::protocol::TouchEvent::TOUCH_POINT_CANCEL; + return true; + } + + NOTREACHED(); + return false; + } +}; + +template <> +class mojo::StructTraits { + public: + static ::remoting::protocol::TouchEvent::TouchEventType event_type( + const ::remoting::protocol::TouchEvent& event) { + return event.event_type(); + } + + static const ::google::protobuf::RepeatedPtrField< + ::remoting::protocol::TouchEventPoint> + touch_points(const ::remoting::protocol::TouchEvent& event) { + return event.touch_points(); + } + + static bool Read(remoting::mojom::TouchEventDataView data_view, + ::remoting::protocol::TouchEvent* out_event); +}; + } // namespace mojo #endif // REMOTING_HOST_MOJOM_REMOTING_MOJOM_TRAITS_H_ diff --git a/remoting/host/mojom/wrapped_primitives.mojom b/remoting/host/mojom/wrapped_primitives.mojom new file mode 100644 index 0000000000000..4d63ae73fc772 --- /dev/null +++ b/remoting/host/mojom/wrapped_primitives.mojom @@ -0,0 +1,24 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module remoting.mojom; + +// Wrapping these primitive types in a struct so they can be used to represent +// optional primitive fields in Mojo structures for use cases like serializing +// and deserializing protobufs. + +// TODO(https://crbug.com/657632): Remove this once a supported mechanism is +// available. + +struct Bool { + bool value; +}; + +struct Float { + float value; +}; + +struct Int32 { + int32 value; +}; diff --git a/remoting/proto/event.proto b/remoting/proto/event.proto index fd0bc74271a1a..d841ee8fe3296 100644 --- a/remoting/proto/event.proto +++ b/remoting/proto/event.proto @@ -128,6 +128,7 @@ message TouchEvent { // treated as an ACTION_UP (END) event but might not perform the exact same // actions as a normal ACTION_UP event. enum TouchEventType { + TOUCH_POINT_UNDEFINED = 0; TOUCH_POINT_START = 1; TOUCH_POINT_MOVE = 2; TOUCH_POINT_END = 3;