diff options
author | miletus@chromium.org <miletus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-13 19:18:38 +0000 |
---|---|---|
committer | miletus@chromium.org <miletus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-13 19:18:38 +0000 |
commit | 06b20fe95e55c66ac56d1ea09a5fcc32cf4c7373 (patch) | |
tree | 582fd3115e4d5c99bfbd5e2a6acfda468efa20d6 /chromeos/display | |
parent | 4db9800ef1f1c4062c6a00fcd175e38988c30087 (diff) | |
download | chromium_src-06b20fe95e55c66ac56d1ea09a5fcc32cf4c7373.zip chromium_src-06b20fe95e55c66ac56d1ea09a5fcc32cf4c7373.tar.gz chromium_src-06b20fe95e55c66ac56d1ea09a5fcc32cf4c7373.tar.bz2 |
Correct X event CTM calculation for multi-monitor case
When there are multi-monitors, X will scale the touch event's
coordinate from device coordinate to screen coordinate. We then
in Chrome compute a CTM that will scale/translate the X events
to the correct RootWindow's bounds. The scaling in X are done
in floating points but when finally converted to ui::TouchEvent's
location as integers, there could be +-1 happening which could
make the touch events appear as out of bound of any RootWindow
and thus get lost. One symptom is that when move fingers to the
upper edge of external monitor and lift, the touch release event
could be lost and the finger seems get stuck on the screen.
We have a small fix in calculating CTM X/Y scale/offset by using
framebuffer_width/height - 1 instead of framebuffer_width/height.
Also when converting X event's coordinate to ui::TouchEvent's
location, use rounding instead of static_cast<int>(double),
which seems more correct.
BUG=315942
TEST=Connect Pixel to external touch monitor, turn on HUD projection
by Ctrl + Shift + 9. Move fingers on external monitor to edges
and lift. Make sure no fingers get stuck at the edges.
Review URL: https://codereview.chromium.org/69653002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@234871 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos/display')
-rw-r--r-- | chromeos/display/output_configurator.cc | 51 | ||||
-rw-r--r-- | chromeos/display/output_configurator.h | 9 | ||||
-rw-r--r-- | chromeos/display/output_configurator_unittest.cc | 40 |
3 files changed, 90 insertions, 10 deletions
diff --git a/chromeos/display/output_configurator.cc b/chromeos/display/output_configurator.cc index 1a3cc31..67da273 100644 --- a/chromeos/display/output_configurator.cc +++ b/chromeos/display/output_configurator.cc @@ -932,16 +932,8 @@ bool OutputConfigurator::EnterState( for (size_t i = 0; i < updated_outputs.size(); ++i) { OutputSnapshot* output = &updated_outputs[i]; - if (output->touch_device_id) { - const ModeInfo* mode_info = - GetModeInfo(*output, output->selected_mode); - DCHECK(mode_info); - CoordinateTransformation* ctm = &(output->transform); - ctm->x_scale = static_cast<float>(mode_info->width) / width; - ctm->x_offset = static_cast<float>(output->x) / width; - ctm->y_scale = static_cast<float>(mode_info->height) / height; - ctm->y_offset = static_cast<float>(output->y) / height; - } + if (output->touch_device_id) + output->transform = GetExtendedModeCTM(*output, width, height); } break; } @@ -1040,6 +1032,45 @@ OutputConfigurator::GetMirrorModeCTM( return ctm; // Same aspect ratio - return identity } +OutputConfigurator::CoordinateTransformation +OutputConfigurator::GetExtendedModeCTM( + const OutputConfigurator::OutputSnapshot& output, + int framebuffer_width, + int framebuffer_height) { + CoordinateTransformation ctm; // Default to identity + const ModeInfo* mode_info = GetModeInfo(output, output.selected_mode); + DCHECK(mode_info); + if (!mode_info) + return ctm; + // An example of how to calculate the CTM. + // Suppose we have 2 monitors, the first one has size 1366 x 768. + // The second one has size 2560 x 1600 + // The total size of framebuffer is 2560 x 2428 + // where 2428 = 768 + 60 (hidden gap) + 1600 + // and the sceond monitor is translated to Point (0, 828) in the + // framebuffer. + // X will first map input event location to [0, 2560) x [0, 2428), + // then apply CTM on it. + // So to compute CTM, for monitor1, we have + // x_scale = (1366 - 1) / (2560 - 1) + // x_offset = 0 / (2560 - 1) + // y_scale = (768 - 1) / (2428 - 1) + // y_offset = 0 / (2428 -1) + // For Monitor 2, we have + // x_scale = (2560 - 1) / (2560 - 1) + // x_offset = 0 / (2560 - 1) + // y_scale = (1600 - 1) / (2428 - 1) + // y_offset = 828 / (2428 -1) + // See the unittest OutputConfiguratorTest.CTMForMultiScreens. + ctm.x_scale = + static_cast<float>(mode_info->width - 1) / (framebuffer_width - 1); + ctm.x_offset = static_cast<float>(output.x) / (framebuffer_width - 1); + ctm.y_scale = + static_cast<float>(mode_info->height - 1) / (framebuffer_height - 1); + ctm.y_offset = static_cast<float>(output.y) / (framebuffer_height - 1); + return ctm; +} + float OutputConfigurator::GetMirroredDisplayAreaRatio( const OutputConfigurator::OutputSnapshot& output) { float area_ratio = 1.0f; diff --git a/chromeos/display/output_configurator.h b/chromeos/display/output_configurator.h index ef5085e..c927519 100644 --- a/chromeos/display/output_configurator.h +++ b/chromeos/display/output_configurator.h @@ -487,6 +487,15 @@ class CHROMEOS_EXPORT OutputConfigurator CoordinateTransformation GetMirrorModeCTM( const OutputConfigurator::OutputSnapshot& output); + // Computes the relevant transformation for extended mode. + // |output| is the output on which extended mode is being applied. + // |width| and |height| are the width and height of the combined framebuffer. + // Returns the transformation or identity if computations fail. + CoordinateTransformation GetExtendedModeCTM( + const OutputConfigurator::OutputSnapshot& output, + int framebuffer_width, + int frame_buffer_height); + // Returns the ratio between mirrored mode area and native mode area: // (mirror_mode_width * mirrow_mode_height) / (native_width * native_height) float GetMirroredDisplayAreaRatio( diff --git a/chromeos/display/output_configurator_unittest.cc b/chromeos/display/output_configurator_unittest.cc index 28f0553..571df38 100644 --- a/chromeos/display/output_configurator_unittest.cc +++ b/chromeos/display/output_configurator_unittest.cc @@ -4,6 +4,7 @@ #include "chromeos/display/output_configurator.h" +#include <cmath> #include <cstdarg> #include <map> #include <string> @@ -128,6 +129,11 @@ class TestDelegate : public OutputConfigurator::Delegate { return actions; } + const OutputConfigurator::CoordinateTransformation& get_ctm( + int touch_device_id) { + return ctms_[touch_device_id]; + } + // OutputConfigurator::Delegate overrides: virtual void InitXRandRExtension(int* event_base) OVERRIDE { AppendAction(kInitXRandR); @@ -171,6 +177,7 @@ class TestDelegate : public OutputConfigurator::Delegate { int touch_device_id, const OutputConfigurator::CoordinateTransformation& ctm) OVERRIDE { AppendAction(GetCTMAction(touch_device_id, ctm)); + ctms_[touch_device_id] = ctm; } virtual void SendProjectingStateToPowerManager(bool projecting) OVERRIDE { AppendAction(projecting ? kProjectingOn : kProjectingOff); @@ -207,6 +214,9 @@ class TestDelegate : public OutputConfigurator::Delegate { std::map<RRMode, ModeDetails> modes_; + // Most-recently-configured transformation matrices, keyed by touch device ID. + std::map<int, OutputConfigurator::CoordinateTransformation> ctms_; + // Outputs to be returned by GetOutputs(). std::vector<OutputConfigurator::OutputSnapshot> outputs_; @@ -1223,4 +1233,34 @@ TEST_F(OutputConfiguratorTest, OutputProtectionTwoClients) { delegate_->GetActionsAndClear()); } +TEST_F(OutputConfiguratorTest, CTMForMultiScreens) { + outputs_[0].touch_device_id = 1; + outputs_[1].touch_device_id = 2; + + UpdateOutputs(2, false); + configurator_.Init(false); + state_controller_.set_state(STATE_DUAL_EXTENDED); + configurator_.Start(0); + + const int kDualHeight = + kSmallModeHeight + OutputConfigurator::kVerticalGap + kBigModeHeight; + const int kDualWidth = kBigModeWidth; + + OutputConfigurator::CoordinateTransformation ctm1 = delegate_->get_ctm(1); + OutputConfigurator::CoordinateTransformation ctm2 = delegate_->get_ctm(2); + + EXPECT_EQ(kSmallModeHeight - 1, round((kDualHeight - 1) * ctm1.y_scale)); + EXPECT_EQ(0, round((kDualHeight - 1) * ctm1.y_offset)); + + EXPECT_EQ(kBigModeHeight - 1, round((kDualHeight - 1) * ctm2.y_scale)); + EXPECT_EQ(kSmallModeHeight + OutputConfigurator::kVerticalGap, + round((kDualHeight - 1) * ctm2.y_offset)); + + EXPECT_EQ(kSmallModeWidth - 1, round((kDualWidth - 1) * ctm1.x_scale)); + EXPECT_EQ(0, round((kDualWidth - 1) * ctm1.x_offset)); + + EXPECT_EQ(kBigModeWidth - 1, round((kDualWidth - 1) * ctm2.x_scale)); + EXPECT_EQ(0, round((kDualWidth - 1) * ctm2.x_offset)); +} + } // namespace chromeos |