| // Copyright 2015 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. |
| |
| #include "remoting/client/input/touch_input_scaler.h" |
| |
| #include "base/check_op.h" |
| #include "base/cxx17_backports.h" |
| #include "remoting/proto/event.pb.h" |
| |
| namespace remoting { |
| |
| using protocol::TouchEvent; |
| using protocol::TouchEventPoint; |
| |
| namespace { |
| |
| // |value| is the number to be scaled. |output_max| is the output desktop's max |
| // height or width. |input_max| is the input desktop's max height or width. |
| float Scale(float value, int output_max, int input_max) { |
| DCHECK_GT(output_max, 0); |
| DCHECK_GT(input_max, 0); |
| value *= output_max; |
| value /= input_max; |
| return value; |
| } |
| |
| // Same as Scale() but |value| will be scaled and clamped using |output_max| and |
| // |input_max|. |
| float ScaleAndClamp(float value, int output_max, int input_max) { |
| value = Scale(value, output_max, input_max); |
| return base::clamp(value, 0.0f, static_cast<float>(output_max)); |
| } |
| |
| } // namespace |
| |
| TouchInputScaler::TouchInputScaler(InputStub* input_stub) |
| : InputFilter(input_stub) {} |
| |
| TouchInputScaler::~TouchInputScaler() = default; |
| |
| void TouchInputScaler::InjectTouchEvent(const TouchEvent& event) { |
| if (input_size_.is_empty() || output_size_.is_empty()) |
| return; |
| |
| // We scale based on the maximum input & output coordinates, rather than the |
| // input and output sizes, so that it's possible to reach the edge of the |
| // output when up-scaling. We also take care to round up or down correctly, |
| // which is important when down-scaling. |
| TouchEvent out_event(event); |
| for (int i = 0; i < out_event.touch_points().size(); ++i) { |
| TouchEventPoint* point = out_event.mutable_touch_points(i); |
| if (point->has_x() || point->has_y()) { |
| DCHECK(point->has_x() && point->has_y()); |
| point->set_x( |
| ScaleAndClamp(point->x(), output_size_.width(), input_size_.width())); |
| point->set_y(ScaleAndClamp(point->y(), output_size_.height(), |
| input_size_.height())); |
| } |
| |
| // Also scale the touch size. Without scaling, the size on the host will not |
| // be right. |
| // For example |
| // Suppose: |
| // - No size scaling. |
| // - Client is a HiDPI Chromebook device. |
| // - Host is running on a HiDPI Windows device. |
| // With the configuration above, the client will send the logical touch |
| // size to the host, therefore it will be smaller on the host. |
| // This is because a HiDPI Chromebook device (e.g. Pixel) has 2 by 2 |
| // physical pixel mapped to a logical pixel. |
| // With scaling, the size would be the same. |
| // Note that there's no need to clamp the touch point size. For example on |
| // a Nexus4 device, part of the touch circle falls outside the screen on |
| // edges but still functions correctly. |
| if (point->has_radius_x() || point->has_radius_y()) { |
| DCHECK(point->has_radius_x() && point->has_radius_y()); |
| point->set_radius_x( |
| Scale(point->radius_x(), output_size_.width(), input_size_.width())); |
| point->set_radius_y(Scale(point->radius_y(), output_size_.height(), |
| input_size_.height())); |
| } |
| } |
| |
| InputFilter::InjectTouchEvent(out_event); |
| } |
| |
| } // namespace remoting |