summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
authormiletus@chromium.org <miletus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-09 17:55:41 +0000
committermiletus@chromium.org <miletus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-09 17:55:41 +0000
commit02418cca1bcf86ebd4cdec162d81c1ea10c7bed7 (patch)
tree84cb369b12b648da4fd19aee0b42a4c48cc3c7b1 /ash
parentb74340984753c1b4bfa93290952b3f3d6208b1b8 (diff)
downloadchromium_src-02418cca1bcf86ebd4cdec162d81c1ea10c7bed7.zip
chromium_src-02418cca1bcf86ebd4cdec162d81c1ea10c7bed7.tar.gz
chromium_src-02418cca1bcf86ebd4cdec162d81c1ea10c7bed7.tar.bz2
Move touch CTM from X into Chrome
Currently we compute the touch CTM in OutputConfigurator and push that into X. This CL makes computing the touch CTM in DisplayController, and pushing it into WindowTreeHostX11. This moves the functionality of touch CTM from X into Chrome. Basically, when there is output configuration change, we compute the TouchCTM for each touch device, and push the TouchCTM into the WindowTreeHostX11 that is associated with the touchscreen. Then when X events reaching root window, we use the CTM to map the events coordinate in framebuffer space into the root window's coordinate space. BUG=351019, chrome-os-partner:25788 TEST=tested on Pixel/Clapper with external touch/non-touch displays on both extended/mirror mode. Touch events are correctly mapped to chrome window or discarded if it is from blank region from letterboxing/pillarboxing mirror mode. Review URL: https://codereview.chromium.org/191223007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269371 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r--ash/DEPS3
-rw-r--r--ash/ash.gyp9
-rw-r--r--ash/display/display_change_observer_chromeos.cc1
-rw-r--r--ash/display/display_controller.cc3
-rw-r--r--ash/display/display_info.cc9
-rw-r--r--ash/display/display_info.h7
-rw-r--r--ash/host/ash_window_tree_host.h7
-rw-r--r--ash/host/ash_window_tree_host_x11.cc250
-rw-r--r--ash/host/ash_window_tree_host_x11.h19
-rw-r--r--ash/host/ash_window_tree_host_x11_unittest.cc82
-rw-r--r--ash/shell.cc12
-rw-r--r--ash/shell.h7
-rw-r--r--ash/touch/touch_transformer_controller.cc225
-rw-r--r--ash/touch/touch_transformer_controller.h55
-rw-r--r--ash/touch/touch_transformer_controller_unittest.cc206
15 files changed, 654 insertions, 241 deletions
diff --git a/ash/DEPS b/ash/DEPS
index 5f55d5c..86c5214 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -21,4 +21,7 @@ specific_include_rules = {
"root_window_controller\.*": [
"+ash/host"
],
+ "touch_transformer_controller\.*": [
+ "+ash/host"
+ ],
}
diff --git a/ash/ash.gyp b/ash/ash.gyp
index 1b8212a..7c2ece2 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -519,6 +519,8 @@
'touch/touch_hud_projection.h',
'touch/touch_observer_hud.cc',
'touch/touch_observer_hud.h',
+ 'touch/touch_transformer_controller.cc',
+ 'touch/touch_transformer_controller.h',
'touch/touch_uma.cc',
'touch/touch_uma.h',
'volume_control_delegate.h',
@@ -700,6 +702,8 @@
['exclude', 'accelerators/magnifier_key_scroller.h'],
['exclude', 'accelerators/spoken_feedback_toggler.cc'],
['exclude', 'accelerators/spoken_feedback_toggler.h'],
+ ['exclude', 'touch/touch_transformer_controller.cc'],
+ ['exclude', 'touch/touch_transformer_controller.h'],
],
}, { # else: use_x11==1
'dependencies': [
@@ -725,6 +729,8 @@
['exclude', 'system/tray/media_security/media_capture_observer.h'],
['exclude', 'system/tray/media_security/multi_profile_media_tray_item.cc'],
['exclude', 'system/tray/media_security/multi_profile_media_tray_item.h'],
+ ['exclude', 'touch/touch_transformer_controller.cc'],
+ ['exclude', 'touch/touch_transformer_controller.h'],
],
}],
],
@@ -973,6 +979,7 @@
'test/ash_unittests.cc',
'tooltips/tooltip_controller_unittest.cc',
'touch/touch_observer_hud_unittest.cc',
+ 'touch/touch_transformer_controller_unittest.cc',
'wm/app_list_controller_unittest.cc',
'wm/ash_native_cursor_manager_unittest.cc',
'wm/dock/docked_window_layout_manager_unittest.cc',
@@ -1047,6 +1054,7 @@
'sources/': [
['exclude', 'accelerators/magnifier_key_scroller_unittest.cc'],
['exclude', 'accelerators/spoken_feedback_toggler_unittest.cc'],
+ ['exclude', 'touch/touch_transformer_controller_unittest.cc'],
],
}],
['chromeos==1', {
@@ -1064,6 +1072,7 @@
}, { # else: chromeos!=1
'sources/': [
['exclude', 'display/resolution_notification_controller_unittest.cc'],
+ ['exclude', 'touch/touch_transformer_controller_unittest.cc'],
],
}],
['OS=="linux" and component=="shared_library" and use_allocator!="none"', {
diff --git a/ash/display/display_change_observer_chromeos.cc b/ash/display/display_change_observer_chromeos.cc
index ad6b029..40a1a37 100644
--- a/ash/display/display_change_observer_chromeos.cc
+++ b/ash/display/display_change_observer_chromeos.cc
@@ -152,6 +152,7 @@ void DisplayChangeObserver::OnDisplayModeChanged(
new_info.set_touch_support(state.touch_device_id == 0 ?
gfx::Display::TOUCH_SUPPORT_UNAVAILABLE :
gfx::Display::TOUCH_SUPPORT_AVAILABLE);
+ new_info.set_touch_device_id(state.touch_device_id);
new_info.set_available_color_profiles(
Shell::GetInstance()
->display_configurator()
diff --git a/ash/display/display_controller.cc b/ash/display/display_controller.cc
index c8cad9e..69e36b7 100644
--- a/ash/display/display_controller.cc
+++ b/ash/display/display_controller.cc
@@ -247,8 +247,6 @@ void DisplayController::Start() {
new VirtualKeyboardWindowController);
Shell::GetScreen()->AddObserver(this);
Shell::GetInstance()->display_manager()->set_delegate(this);
-
- FOR_EACH_OBSERVER(Observer, observers_, OnDisplaysInitialized());
}
void DisplayController::Shutdown() {
@@ -294,6 +292,7 @@ void DisplayController::InitDisplays() {
}
}
UpdateHostWindowNames();
+ FOR_EACH_OBSERVER(Observer, observers_, OnDisplaysInitialized());
}
void DisplayController::AddObserver(Observer* observer) {
diff --git a/ash/display/display_info.cc b/ash/display/display_info.cc
index 3159853..9682efa 100644
--- a/ash/display/display_info.cc
+++ b/ash/display/display_info.cc
@@ -179,6 +179,7 @@ DisplayInfo::DisplayInfo()
has_overscan_(false),
rotation_(gfx::Display::ROTATE_0),
touch_support_(gfx::Display::TOUCH_SUPPORT_UNKNOWN),
+ touch_device_id_(0),
device_scale_factor_(1.0f),
overscan_insets_in_dip_(0, 0, 0, 0),
configured_ui_scale_(1.0f),
@@ -194,6 +195,7 @@ DisplayInfo::DisplayInfo(int64 id,
has_overscan_(has_overscan),
rotation_(gfx::Display::ROTATE_0),
touch_support_(gfx::Display::TOUCH_SUPPORT_UNKNOWN),
+ touch_device_id_(0),
device_scale_factor_(1.0f),
overscan_insets_in_dip_(0, 0, 0, 0),
configured_ui_scale_(1.0f),
@@ -215,6 +217,7 @@ void DisplayInfo::Copy(const DisplayInfo& native_info) {
device_scale_factor_ = native_info.device_scale_factor_;
display_modes_ = native_info.display_modes_;
touch_support_ = native_info.touch_support_;
+ touch_device_id_ = native_info.touch_device_id_;
// Copy overscan_insets_in_dip_ if it's not empty. This is for test
// cases which use "/o" annotation which sets the overscan inset
@@ -295,7 +298,8 @@ std::string DisplayInfo::ToString() const {
int rotation_degree = static_cast<int>(rotation_) * 90;
return base::StringPrintf(
"DisplayInfo[%lld] native bounds=%s, size=%s, scale=%f, "
- "overscan=%s, rotation=%d, ui-scale=%f, touchscreen=%s",
+ "overscan=%s, rotation=%d, ui-scale=%f, touchscreen=%s, "
+ "touch-device-id=%d",
static_cast<long long int>(id_),
bounds_in_native_.ToString().c_str(),
size_in_pixel_.ToString().c_str(),
@@ -307,7 +311,8 @@ std::string DisplayInfo::ToString() const {
? "yes"
: touch_support_ == gfx::Display::TOUCH_SUPPORT_UNAVAILABLE
? "no"
- : "unknown");
+ : "unknown",
+ touch_device_id_);
}
std::string DisplayInfo::ToFullString() const {
diff --git a/ash/display/display_info.h b/ash/display/display_info.h
index 38e16d8..62f9210 100644
--- a/ash/display/display_info.h
+++ b/ash/display/display_info.h
@@ -103,6 +103,9 @@ class ASH_EXPORT DisplayInfo {
}
gfx::Display::TouchSupport touch_support() const { return touch_support_; }
+ void set_touch_device_id(int id) { touch_device_id_ = id; }
+ int touch_device_id() const { return touch_device_id_; }
+
// Gets/Sets the device scale factor of the display.
float device_scale_factor() const { return device_scale_factor_; }
void set_device_scale_factor(float scale) { device_scale_factor_ = scale; }
@@ -201,6 +204,10 @@ class ASH_EXPORT DisplayInfo {
gfx::Display::Rotation rotation_;
gfx::Display::TouchSupport touch_support_;
+ // If the display is also a touch device, it will have a positive
+ // |touch_device_id_|. Otherwise |touch_device_id_| is 0.
+ int touch_device_id_;
+
// This specifies the device's pixel density. (For example, a
// display whose DPI is higher than the threshold is considered to have
// device_scale_factor = 2.0 on Chrome OS). This is used by the
diff --git a/ash/host/ash_window_tree_host.h b/ash/host/ash_window_tree_host.h
index 9962b89..7e791fe 100644
--- a/ash/host/ash_window_tree_host.h
+++ b/ash/host/ash_window_tree_host.h
@@ -42,6 +42,13 @@ class ASH_EXPORT AshWindowTreeHost {
scoped_ptr<RootWindowTransformer> transformer) = 0;
virtual aura::WindowTreeHost* AsWindowTreeHost() = 0;
+
+ // Updates the display IDs associated with this root window.
+ // A root window can be associated with up to 2 display IDs (e.g. in mirror
+ // mode dual monitors case). If the root window is only associated with one
+ // display id, then the other id should be set to
+ // gfx::Display::kInvalidDisplayID.
+ virtual void UpdateDisplayID(int64 id1, int64 id2) {};
};
} // namespace ash
diff --git a/ash/host/ash_window_tree_host_x11.cc b/ash/host/ash_window_tree_host_x11.cc
index e49e961..370eacf 100644
--- a/ash/host/ash_window_tree_host_x11.cc
+++ b/ash/host/ash_window_tree_host_x11.cc
@@ -14,162 +14,26 @@
#include "ash/host/root_window_transformer.h"
#include "base/basictypes.h"
-#include "base/command_line.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/event.h"
-#include "ui/events/event_switches.h"
#include "ui/events/event_utils.h"
-#include "ui/events/platform/platform_event_observer.h"
-#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/events/x/device_data_manager.h"
#include "ui/events/x/device_list_cache_x.h"
#include "ui/events/x/touch_factory_x11.h"
-
#include "ui/gfx/rect.h"
#include "ui/gfx/screen.h"
namespace ash {
-// Accomplishes 2 tasks concerning touch event calibration:
-// 1. Being a message-pump observer,
-// routes all the touch events to the X root window,
-// where they can be calibrated later.
-// 2. Has the Calibrate method that does the actual bezel calibration,
-// when invoked from X root window's event dispatcher.
-class AshWindowTreeHostX11::TouchEventCalibrate
- : public ui::PlatformEventObserver {
- public:
- TouchEventCalibrate() : left_(0), right_(0), top_(0), bottom_(0) {
- if (ui::PlatformEventSource::GetInstance())
- ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this);
-#if defined(USE_XI2_MT)
- std::vector<std::string> parts;
- if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kTouchCalibration),
- ",",
- &parts) >= 4) {
- if (!base::StringToInt(parts[0], &left_))
- DLOG(ERROR) << "Incorrect left border calibration value passed.";
- if (!base::StringToInt(parts[1], &right_))
- DLOG(ERROR) << "Incorrect right border calibration value passed.";
- if (!base::StringToInt(parts[2], &top_))
- DLOG(ERROR) << "Incorrect top border calibration value passed.";
- if (!base::StringToInt(parts[3], &bottom_))
- DLOG(ERROR) << "Incorrect bottom border calibration value passed.";
- }
-#endif // defined(USE_XI2_MT)
- }
-
- virtual ~TouchEventCalibrate() {
- if (ui::PlatformEventSource::GetInstance())
- ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this);
- }
-
- // Modify the location of the |event|,
- // expanding it from |bounds| to (|bounds| + bezels).
- // Required when touchscreen is bigger than screen (i.e. has bezels),
- // because we receive events in touchscreen coordinates,
- // which need to be expanded when converting to screen coordinates,
- // so that location on bezels will be outside of screen area.
- void Calibrate(ui::TouchEvent* event, const gfx::Rect& bounds) {
-#if defined(USE_XI2_MT)
- int x = event->x();
- int y = event->y();
-
- if (!left_ && !right_ && !top_ && !bottom_)
- return;
-
- const int resolution_x = bounds.width();
- const int resolution_y = bounds.height();
- // The "grace area" (10% in this case) is to make it easier for the user to
- // navigate to the corner.
- const double kGraceAreaFraction = 0.1;
- if (left_ || right_) {
- // Offset the x position to the real
- x -= left_;
- // Check if we are in the grace area of the left side.
- // Note: We might not want to do this when the gesture is locked?
- if (x < 0 && x > -left_ * kGraceAreaFraction)
- x = 0;
- // Check if we are in the grace area of the right side.
- // Note: We might not want to do this when the gesture is locked?
- if (x > resolution_x - left_ &&
- x < resolution_x - left_ + right_ * kGraceAreaFraction)
- x = resolution_x - left_;
- // Scale the screen area back to the full resolution of the screen.
- x = (x * resolution_x) / (resolution_x - (right_ + left_));
- }
- if (top_ || bottom_) {
- // When there is a top bezel we add our border,
- y -= top_;
-
- // Check if we are in the grace area of the top side.
- // Note: We might not want to do this when the gesture is locked?
- if (y < 0 && y > -top_ * kGraceAreaFraction)
- y = 0;
-
- // Check if we are in the grace area of the bottom side.
- // Note: We might not want to do this when the gesture is locked?
- if (y > resolution_y - top_ &&
- y < resolution_y - top_ + bottom_ * kGraceAreaFraction)
- y = resolution_y - top_;
- // Scale the screen area back to the full resolution of the screen.
- y = (y * resolution_y) / (resolution_y - (bottom_ + top_));
- }
-
- // Set the modified coordinate back to the event.
- if (event->root_location() == event->location()) {
- // Usually those will be equal,
- // if not, I am not sure what the correct value should be.
- event->set_root_location(gfx::Point(x, y));
- }
- event->set_location(gfx::Point(x, y));
-#endif // defined(USE_XI2_MT)
- }
-
- private:
- // ui::PlatformEventObserver:
- virtual void WillProcessEvent(const ui::PlatformEvent& event) OVERRIDE {
-#if defined(USE_XI2_MT)
- if (event->type == GenericEvent &&
- (event->xgeneric.evtype == XI_TouchBegin ||
- event->xgeneric.evtype == XI_TouchUpdate ||
- event->xgeneric.evtype == XI_TouchEnd)) {
- XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data);
- xievent->event = xievent->root;
- xievent->event_x = xievent->root_x;
- xievent->event_y = xievent->root_y;
- }
-#endif // defined(USE_XI2_MT)
- }
-
- virtual void DidProcessEvent(const ui::PlatformEvent& event) OVERRIDE {}
-
- // The difference in screen's native resolution pixels between
- // the border of the touchscreen and the border of the screen,
- // aka bezel sizes.
- int left_;
- int right_;
- int top_;
- int bottom_;
-
- DISALLOW_COPY_AND_ASSIGN(TouchEventCalibrate);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// AshWindowTreeHostX11
-
AshWindowTreeHostX11::AshWindowTreeHostX11(const gfx::Rect& initial_bounds)
: WindowTreeHostX11(initial_bounds),
- is_internal_display_(false),
- touch_calibrate_(new TouchEventCalibrate),
- transformer_helper_(this) {
+ transformer_helper_(this),
+ display_ids_(std::make_pair(gfx::Display::kInvalidDisplayID,
+ gfx::Display::kInvalidDisplayID)) {
aura::Env::GetInstance()->AddObserver(this);
}
@@ -247,7 +111,6 @@ void AshWindowTreeHostX11::UnConfineCursor() {
void AshWindowTreeHostX11::SetRootWindowTransformer(
scoped_ptr<RootWindowTransformer> transformer) {
transformer_helper_.SetRootWindowTransformer(transformer.Pass());
- UpdateIsInternalDisplay();
if (pointer_barriers_) {
UnConfineCursor();
ConfineCursorToRootWindow();
@@ -256,9 +119,13 @@ void AshWindowTreeHostX11::SetRootWindowTransformer(
aura::WindowTreeHost* AshWindowTreeHostX11::AsWindowTreeHost() { return this; }
+void AshWindowTreeHostX11::UpdateDisplayID(int64 id1, int64 id2) {
+ display_ids_.first = id1;
+ display_ids_.second = id2;
+}
+
void AshWindowTreeHostX11::SetBounds(const gfx::Rect& bounds) {
WindowTreeHostX11::SetBounds(bounds);
- UpdateIsInternalDisplay();
if (pointer_barriers_) {
UnConfineCursor();
ConfineCursorToRootWindow();
@@ -288,13 +155,8 @@ void AshWindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) {
void AshWindowTreeHostX11::OnWindowInitialized(aura::Window* window) {}
void AshWindowTreeHostX11::OnHostInitialized(aura::WindowTreeHost* host) {
- // UpdateIsInternalDisplay relies on RootWindowSettings' display_id being set
- // available by the time WED::Init is called. (set in
- // DisplayManager::CreateRootWindowForDisplay)
- // Ready when NotifyHostInitialized is called from WED::Init.
if (host != AsWindowTreeHost())
return;
- UpdateIsInternalDisplay();
// We have to enable Tap-to-click by default because the cursor is set to
// visible in Shell::InitRootWindowController.
@@ -302,8 +164,6 @@ void AshWindowTreeHostX11::OnHostInitialized(aura::WindowTreeHost* host) {
}
void AshWindowTreeHostX11::OnConfigureNotify() {
- UpdateIsInternalDisplay();
-
// Always update barrier and mouse location because |bounds_| might
// have already been updated in |SetBounds|.
if (pointer_barriers_) {
@@ -312,62 +172,68 @@ void AshWindowTreeHostX11::OnConfigureNotify() {
}
}
-void AshWindowTreeHostX11::TranslateAndDispatchLocatedEvent(
- ui::LocatedEvent* event) {
- switch (event->type()) {
+bool AshWindowTreeHostX11::CanDispatchEvent(const ui::PlatformEvent& event) {
+ if(!WindowTreeHostX11::CanDispatchEvent(event))
+ return false;
+ XEvent* xev = event;
+ XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev->xcookie.data);
+ ui::EventType type = ui::EventTypeFromNative(xev);
+ // For touch event, check if the root window is residing on the according
+ // touch display.
+ switch (type) {
case ui::ET_TOUCH_MOVED:
case ui::ET_TOUCH_PRESSED:
case ui::ET_TOUCH_CANCELLED:
case ui::ET_TOUCH_RELEASED: {
- ui::TouchEvent* touchev = static_cast<ui::TouchEvent*>(event);
- if (base::SysInfo::IsRunningOnChromeOS()) {
- // X maps the touch-surface to the size of the X root-window.
- // In multi-monitor setup, Coordinate Transformation Matrix
- // repositions the touch-surface onto part of X root-window
- // containing aura root-window corresponding to the touchscreen.
- // However, if aura root-window has non-zero origin,
- // we need to relocate the event into aura root-window coordinates.
- touchev->Relocate(bounds().origin());
-#if defined(USE_XI2_MT)
- if (is_internal_display_)
- touch_calibrate_->Calibrate(touchev, bounds());
-#endif // defined(USE_XI2_MT)
+#if defined(OS_CHROMEOS)
+ int64 touch_display_id =
+ ui::DeviceDataManager::GetInstance()->GetDisplayForTouchDevice(
+ xiev->deviceid);
+ // If we don't have record of display id for this touch device, check
+ // that if the event is within the bound of the root window. Note
+ // that in multi-monitor case, the event position is in framebuffer
+ // space so the bounds check will not work so well.
+ if (touch_display_id == gfx::Display::kInvalidDisplayID) {
+ if (base::SysInfo::IsRunningOnChromeOS() &&
+ !bounds().Contains(ui::EventLocationFromNative(xev)))
+ return false;
+ } else if (touch_display_id != display_ids_.first &&
+ touch_display_id != display_ids_.second) {
+ return false;
}
- break;
+#endif // defined(OS_CHROMEOS)
+ return true;
}
- default: {
- aura::Window* root_window = window();
- aura::client::ScreenPositionClient* screen_position_client =
- aura::client::GetScreenPositionClient(root_window);
- gfx::Rect local(bounds().size());
+ default:
+ return true;
+ }
+}
- if (screen_position_client && !local.Contains(event->location())) {
- gfx::Point location(event->location());
- // In order to get the correct point in screen coordinates
- // during passive grab, we first need to find on which host window
- // the mouse is on, and find out the screen coordinates on that
- // host window, then convert it back to this host window's coordinate.
- screen_position_client->ConvertHostPointToScreen(root_window,
- &location);
- screen_position_client->ConvertPointFromScreen(root_window, &location);
- ConvertPointToHost(&location);
- event->set_location(location);
- event->set_root_location(location);
- }
- break;
+void AshWindowTreeHostX11::TranslateAndDispatchLocatedEvent(
+ ui::LocatedEvent* event) {
+ if (!event->IsTouchEvent()) {
+ aura::Window* root_window = window();
+ aura::client::ScreenPositionClient* screen_position_client =
+ aura::client::GetScreenPositionClient(root_window);
+ gfx::Rect local(bounds().size());
+
+ if (screen_position_client && !local.Contains(event->location())) {
+ gfx::Point location(event->location());
+ // In order to get the correct point in screen coordinates
+ // during passive grab, we first need to find on which host window
+ // the mouse is on, and find out the screen coordinates on that
+ // host window, then convert it back to this host window's coordinate.
+ screen_position_client->ConvertHostPointToScreen(root_window,
+ &location);
+ screen_position_client->ConvertPointFromScreen(root_window, &location);
+ ConvertPointToHost(&location);
+ event->set_location(location);
+ event->set_root_location(location);
}
}
SendEventToProcessor(event);
}
-void AshWindowTreeHostX11::UpdateIsInternalDisplay() {
- aura::Window* root_window = window();
- gfx::Screen* screen = gfx::Screen::GetScreenFor(root_window);
- gfx::Display display = screen->GetDisplayNearestWindow(root_window);
- DCHECK(display.is_valid());
- is_internal_display_ = display.IsInternal();
-}
-
void AshWindowTreeHostX11::SetCrOSTapPaused(bool state) {
if (!ui::IsXInput2Available())
return;
diff --git a/ash/host/ash_window_tree_host_x11.h b/ash/host/ash_window_tree_host_x11.h
index f690ea8..4e340b2 100644
--- a/ash/host/ash_window_tree_host_x11.h
+++ b/ash/host/ash_window_tree_host_x11.h
@@ -30,6 +30,7 @@ class ASH_EXPORT AshWindowTreeHostX11 : public AshWindowTreeHost,
virtual void SetRootWindowTransformer(
scoped_ptr<RootWindowTransformer> transformer) OVERRIDE;
virtual aura::WindowTreeHost* AsWindowTreeHost() OVERRIDE;
+ virtual void UpdateDisplayID(int64 id1, int64 id2) OVERRIDE;
// aura::WindowTreehost:
virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
@@ -41,6 +42,7 @@ class ASH_EXPORT AshWindowTreeHostX11 : public AshWindowTreeHost,
// aura::WindowTreeHostX11:
virtual void OnConfigureNotify() OVERRIDE;
+ virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE;
virtual void TranslateAndDispatchLocatedEvent(ui::LocatedEvent* event)
OVERRIDE;
@@ -48,24 +50,21 @@ class ASH_EXPORT AshWindowTreeHostX11 : public AshWindowTreeHost,
virtual void OnWindowInitialized(aura::Window* window) OVERRIDE;
virtual void OnHostInitialized(aura::WindowTreeHost* host) OVERRIDE;
- class TouchEventCalibrate;
-
- // Update is_internal_display_ based on the current state.
- void UpdateIsInternalDisplay();
-
// Set the CrOS touchpad "tap paused" property. It is used to temporarily
// turn off the Tap-to-click feature when the mouse pointer is invisible.
void SetCrOSTapPaused(bool state);
- // True if the root host resides on the internal display
- bool is_internal_display_;
-
scoped_ptr<XID[]> pointer_barriers_;
- scoped_ptr<TouchEventCalibrate> touch_calibrate_;
-
TransformerHelper transformer_helper_;
+ // The display IDs associated with this root window.
+ // In single monitor or extended mode dual monitor case, the root window
+ // is associated with one display.
+ // In mirror mode dual monitors case, the root window is associated with
+ // both displays.
+ std::pair<int64, int64> display_ids_;
+
DISALLOW_COPY_AND_ASSIGN(AshWindowTreeHostX11);
};
diff --git a/ash/host/ash_window_tree_host_x11_unittest.cc b/ash/host/ash_window_tree_host_x11_unittest.cc
index efd25c0..12aa7bb 100644
--- a/ash/host/ash_window_tree_host_x11_unittest.cc
+++ b/ash/host/ash_window_tree_host_x11_unittest.cc
@@ -12,6 +12,7 @@
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
+#include "ui/aura/window_tree_host_x11.h"
#include "ui/events/event_processor.h"
#include "ui/events/event_target.h"
#include "ui/events/event_target_iterator.h"
@@ -56,17 +57,17 @@ class RootWindowEventHandler : public ui::EventHandler {
namespace ash {
-typedef aura::test::AuraTestBase WindowTreeHostX11Test;
+typedef aura::test::AuraTestBase AshWindowTreeHostX11Test;
// Send X touch events to one WindowTreeHost. The WindowTreeHost's
// delegate will get corresponding ui::TouchEvent if the touch events
-// are winthin the bound of the WindowTreeHost.
-TEST_F(WindowTreeHostX11Test, DispatchTouchEventToOneRootWindow) {
+// are targeting this WindowTreeHost.
+TEST_F(AshWindowTreeHostX11Test, DispatchTouchEventToOneRootWindow) {
// Fake a ChromeOS running env.
const char* kLsbRelease = "CHROMEOS_RELEASE_NAME=Chromium OS\n";
base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
- scoped_ptr<AshWindowTreeHostX11> window_tree_host(
+ scoped_ptr<aura::WindowTreeHostX11> window_tree_host(
new AshWindowTreeHostX11(gfx::Rect(0, 0, 2560, 1700)));
window_tree_host->InitHost();
scoped_ptr<RootWindowEventHandler> handler(
@@ -84,7 +85,8 @@ TEST_F(WindowTreeHostX11Test, DispatchTouchEventToOneRootWindow) {
// This touch is out of bounds.
scoped_xevent.InitTouchEvent(
0, XI_TouchBegin, 5, gfx::Point(1500, 2500), valuators);
- window_tree_host->DispatchEvent(scoped_xevent);
+ if (window_tree_host->CanDispatchEvent(scoped_xevent))
+ window_tree_host->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_UNKNOWN, handler->last_touch_type());
EXPECT_EQ(-1, handler->last_touch_id());
EXPECT_EQ(gfx::Point(0, 0), handler->last_touch_location());
@@ -92,21 +94,24 @@ TEST_F(WindowTreeHostX11Test, DispatchTouchEventToOneRootWindow) {
// Following touchs are within bounds and are passed to delegate.
scoped_xevent.InitTouchEvent(
0, XI_TouchBegin, 5, gfx::Point(1500, 1500), valuators);
- window_tree_host->DispatchEvent(scoped_xevent);
+ if (window_tree_host->CanDispatchEvent(scoped_xevent))
+ window_tree_host->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_TOUCH_PRESSED, handler->last_touch_type());
EXPECT_EQ(0, handler->last_touch_id());
EXPECT_EQ(gfx::Point(1500, 1500), handler->last_touch_location());
scoped_xevent.InitTouchEvent(
0, XI_TouchUpdate, 5, gfx::Point(1500, 1600), valuators);
- window_tree_host->DispatchEvent(scoped_xevent);
+ if (window_tree_host->CanDispatchEvent(scoped_xevent))
+ window_tree_host->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_TOUCH_MOVED, handler->last_touch_type());
EXPECT_EQ(0, handler->last_touch_id());
EXPECT_EQ(gfx::Point(1500, 1600), handler->last_touch_location());
scoped_xevent.InitTouchEvent(
0, XI_TouchEnd, 5, gfx::Point(1500, 1600), valuators);
- window_tree_host->DispatchEvent(scoped_xevent);
+ if (window_tree_host->CanDispatchEvent(scoped_xevent))
+ window_tree_host->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_TOUCH_RELEASED, handler->last_touch_type());
EXPECT_EQ(0, handler->last_touch_id());
EXPECT_EQ(gfx::Point(1500, 1600), handler->last_touch_location());
@@ -123,19 +128,19 @@ TEST_F(WindowTreeHostX11Test, DispatchTouchEventToOneRootWindow) {
// Send X touch events to two WindowTreeHost. The WindowTreeHost which is
// the event target of the X touch events should generate the corresponding
// ui::TouchEvent for its delegate.
-TEST_F(WindowTreeHostX11Test, DispatchTouchEventToTwoRootWindow) {
+TEST_F(AshWindowTreeHostX11Test, DispatchTouchEventToTwoRootWindow) {
// Fake a ChromeOS running env.
const char* kLsbRelease = "CHROMEOS_RELEASE_NAME=Chromium OS\n";
base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
- scoped_ptr<AshWindowTreeHostX11> window_tree_host1(
+ scoped_ptr<aura::WindowTreeHostX11> window_tree_host1(
new AshWindowTreeHostX11(gfx::Rect(0, 0, 2560, 1700)));
window_tree_host1->InitHost();
scoped_ptr<RootWindowEventHandler> handler1(
new RootWindowEventHandler(window_tree_host1.get()));
int host2_y_offset = 1700;
- scoped_ptr<AshWindowTreeHostX11> window_tree_host2(
+ scoped_ptr<aura::WindowTreeHostX11> window_tree_host2(
new AshWindowTreeHostX11(gfx::Rect(0, host2_y_offset, 1920, 1080)));
window_tree_host2->InitHost();
scoped_ptr<RootWindowEventHandler> handler2(
@@ -155,74 +160,81 @@ TEST_F(WindowTreeHostX11Test, DispatchTouchEventToTwoRootWindow) {
ui::ScopedXI2Event scoped_xevent;
scoped_xevent.InitTouchEvent(
0, XI_TouchBegin, 5, gfx::Point(1500, 2500), valuators);
- window_tree_host1->DispatchEvent(scoped_xevent);
- window_tree_host2->DispatchEvent(scoped_xevent);
+ if (window_tree_host1->CanDispatchEvent(scoped_xevent))
+ window_tree_host1->DispatchEvent(scoped_xevent);
+ if (window_tree_host2->CanDispatchEvent(scoped_xevent))
+ window_tree_host2->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_UNKNOWN, handler1->last_touch_type());
EXPECT_EQ(-1, handler1->last_touch_id());
EXPECT_EQ(gfx::Point(0, 0), handler1->last_touch_location());
EXPECT_EQ(ui::ET_TOUCH_PRESSED, handler2->last_touch_type());
EXPECT_EQ(0, handler2->last_touch_id());
- EXPECT_EQ(gfx::Point(1500, 2500 - host2_y_offset),
- handler2->last_touch_location());
+ EXPECT_EQ(gfx::Point(1500, 2500), handler2->last_touch_location());
scoped_xevent.InitTouchEvent(
0, XI_TouchBegin, 6, gfx::Point(1600, 2600), valuators);
- window_tree_host1->DispatchEvent(scoped_xevent);
- window_tree_host2->DispatchEvent(scoped_xevent);
+ if (window_tree_host1->CanDispatchEvent(scoped_xevent))
+ window_tree_host1->DispatchEvent(scoped_xevent);
+ if (window_tree_host2->CanDispatchEvent(scoped_xevent))
+ window_tree_host2->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_UNKNOWN, handler1->last_touch_type());
EXPECT_EQ(-1, handler1->last_touch_id());
EXPECT_EQ(gfx::Point(0, 0), handler1->last_touch_location());
EXPECT_EQ(ui::ET_TOUCH_PRESSED, handler2->last_touch_type());
EXPECT_EQ(1, handler2->last_touch_id());
- EXPECT_EQ(gfx::Point(1600, 2600 - host2_y_offset),
- handler2->last_touch_location());
+ EXPECT_EQ(gfx::Point(1600, 2600), handler2->last_touch_location());
scoped_xevent.InitTouchEvent(
0, XI_TouchUpdate, 5, gfx::Point(1500, 2550), valuators);
- window_tree_host1->DispatchEvent(scoped_xevent);
- window_tree_host2->DispatchEvent(scoped_xevent);
+ if (window_tree_host1->CanDispatchEvent(scoped_xevent))
+ window_tree_host1->DispatchEvent(scoped_xevent);
+ if (window_tree_host2->CanDispatchEvent(scoped_xevent))
+ window_tree_host2->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_UNKNOWN, handler1->last_touch_type());
EXPECT_EQ(-1, handler1->last_touch_id());
EXPECT_EQ(gfx::Point(0, 0), handler1->last_touch_location());
EXPECT_EQ(ui::ET_TOUCH_MOVED, handler2->last_touch_type());
EXPECT_EQ(0, handler2->last_touch_id());
- EXPECT_EQ(gfx::Point(1500, 2550 - host2_y_offset),
- handler2->last_touch_location());
+ EXPECT_EQ(gfx::Point(1500, 2550), handler2->last_touch_location());
scoped_xevent.InitTouchEvent(
0, XI_TouchUpdate, 6, gfx::Point(1600, 2650), valuators);
- window_tree_host1->DispatchEvent(scoped_xevent);
- window_tree_host2->DispatchEvent(scoped_xevent);
+ if (window_tree_host1->CanDispatchEvent(scoped_xevent))
+ window_tree_host1->DispatchEvent(scoped_xevent);
+ if (window_tree_host2->CanDispatchEvent(scoped_xevent))
+ window_tree_host2->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_UNKNOWN, handler1->last_touch_type());
EXPECT_EQ(-1, handler1->last_touch_id());
EXPECT_EQ(gfx::Point(0, 0), handler1->last_touch_location());
EXPECT_EQ(ui::ET_TOUCH_MOVED, handler2->last_touch_type());
EXPECT_EQ(1, handler2->last_touch_id());
- EXPECT_EQ(gfx::Point(1600, 2650 - host2_y_offset),
- handler2->last_touch_location());
+ EXPECT_EQ(gfx::Point(1600, 2650), handler2->last_touch_location());
scoped_xevent.InitTouchEvent(
0, XI_TouchEnd, 5, gfx::Point(1500, 2550), valuators);
- window_tree_host1->DispatchEvent(scoped_xevent);
- window_tree_host2->DispatchEvent(scoped_xevent);
+ if (window_tree_host1->CanDispatchEvent(scoped_xevent))
+ window_tree_host1->DispatchEvent(scoped_xevent);
+ if (window_tree_host2->CanDispatchEvent(scoped_xevent))
+ window_tree_host2->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_UNKNOWN, handler1->last_touch_type());
EXPECT_EQ(-1, handler1->last_touch_id());
EXPECT_EQ(gfx::Point(0, 0), handler1->last_touch_location());
EXPECT_EQ(ui::ET_TOUCH_RELEASED, handler2->last_touch_type());
EXPECT_EQ(0, handler2->last_touch_id());
- EXPECT_EQ(gfx::Point(1500, 2550 - host2_y_offset),
- handler2->last_touch_location());
+ EXPECT_EQ(gfx::Point(1500, 2550), handler2->last_touch_location());
+
scoped_xevent.InitTouchEvent(
0, XI_TouchEnd, 6, gfx::Point(1600, 2650), valuators);
- window_tree_host1->DispatchEvent(scoped_xevent);
- window_tree_host2->DispatchEvent(scoped_xevent);
+ if (window_tree_host1->CanDispatchEvent(scoped_xevent))
+ window_tree_host1->DispatchEvent(scoped_xevent);
+ if (window_tree_host2->CanDispatchEvent(scoped_xevent))
+ window_tree_host2->DispatchEvent(scoped_xevent);
EXPECT_EQ(ui::ET_UNKNOWN, handler1->last_touch_type());
EXPECT_EQ(-1, handler1->last_touch_id());
EXPECT_EQ(gfx::Point(0, 0), handler1->last_touch_location());
EXPECT_EQ(ui::ET_TOUCH_RELEASED, handler2->last_touch_type());
EXPECT_EQ(1, handler2->last_touch_id());
- EXPECT_EQ(gfx::Point(1600, 2650 - host2_y_offset),
- handler2->last_touch_location());
+ EXPECT_EQ(gfx::Point(1600, 2650), handler2->last_touch_location());
handler1.reset();
handler2.reset();
diff --git a/ash/shell.cc b/ash/shell.cc
index 8bd4460..c8f65bd 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -114,6 +114,7 @@
#if defined(USE_X11)
#include "ash/accelerators/magnifier_key_scroller.h"
#include "ash/accelerators/spoken_feedback_toggler.h"
+#include "ash/touch/touch_transformer_controller.h"
#include "ui/gfx/x/x11_types.h"
#endif // defined(USE_X11)
#include "ash/ash_constants.h"
@@ -756,6 +757,10 @@ Shell::~Shell() {
#endif
desktop_background_controller_.reset();
+#if defined(OS_CHROMEOS) && defined(USE_X11)
+ touch_transformer_controller_.reset();
+#endif // defined(OS_CHROMEOS) && defined(USE_X11)
+
// This also deletes all RootWindows. Note that we invoke Shutdown() on
// DisplayController before resetting |display_controller_|, since destruction
// of its owned RootWindowControllers relies on the value.
@@ -987,6 +992,13 @@ void Shell::Init() {
base::Unretained(system_tray_delegate_.get()))));
#endif
+#if defined(OS_CHROMEOS) && defined(USE_X11)
+ // Create TouchTransformerController before DisplayController::InitDisplays()
+ // since TouchTransformerController listens on
+ // DisplayController::Observer::OnDisplaysInitialized().
+ touch_transformer_controller_.reset(new TouchTransformerController());
+#endif // defined(OS_CHROMEOS) && defined(USE_X11)
+
display_controller_->InitDisplays();
// It needs to be created after RootWindowController has been created
diff --git a/ash/shell.h b/ash/shell.h
index 718a6c6..6b987606 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -136,6 +136,7 @@ class SystemTray;
class SystemTrayDelegate;
class SystemTrayNotifier;
class ToplevelWindowEventHandler;
+class TouchTransformerController;
class TouchObserverHUD;
class UserActivityDetector;
class UserWallpaperDelegate;
@@ -370,6 +371,11 @@ class ASH_EXPORT Shell : public SystemModalContainerEventFilterDelegate,
DisplayController* display_controller() {
return display_controller_.get();
}
+#if defined(OS_CHROMEOS) && defined(USE_X11)
+ TouchTransformerController* touch_transformer_controller() {
+ return touch_transformer_controller_.get();
+ }
+#endif // defined(OS_CHROMEOS) && defined(USE_X11)
MouseCursorEventFilter* mouse_cursor_filter() {
return mouse_cursor_filter_.get();
}
@@ -714,6 +720,7 @@ class ASH_EXPORT Shell : public SystemModalContainerEventFilterDelegate,
#if defined(USE_X11)
scoped_ptr<ui::EventHandler> magnifier_key_scroll_handler_;
scoped_ptr<ui::EventHandler> speech_feedback_handler_;
+ scoped_ptr<TouchTransformerController> touch_transformer_controller_;
#endif // defined(USE_X11)
#endif // defined(OS_CHROMEOS)
diff --git a/ash/touch/touch_transformer_controller.cc b/ash/touch/touch_transformer_controller.cc
new file mode 100644
index 0000000..87954a3
--- /dev/null
+++ b/ash/touch/touch_transformer_controller.cc
@@ -0,0 +1,225 @@
+// Copyright 2014 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 "ash/touch/touch_transformer_controller.h"
+
+#include "ash/display/display_controller.h"
+#include "ash/display/display_manager.h"
+#include "ash/host/ash_window_tree_host.h"
+#include "ash/root_window_controller.h"
+#include "ash/shell.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/display/chromeos/display_configurator.h"
+#include "ui/display/types/chromeos/display_snapshot.h"
+#include "ui/events/x/device_data_manager.h"
+
+namespace ash {
+
+namespace {
+
+DisplayManager* GetDisplayManager() {
+ return Shell::GetInstance()->display_manager();
+}
+
+} // namespace
+
+// This function computes the extended mode TouchTransformer for
+// |touch_display|. The TouchTransformer maps the touch event position
+// from framebuffer size to the display size.
+gfx::Transform
+TouchTransformerController::GetExtendedModeTouchTransformer(
+ const DisplayInfo& touch_display, const gfx::Size& fb_size) const {
+ gfx::Transform ctm;
+ if (touch_display.touch_device_id() == 0 ||
+ fb_size.width() == 0.0 ||
+ fb_size.height() == 0.0)
+ return ctm;
+ float width = touch_display.bounds_in_native().width();
+ float height = touch_display.bounds_in_native().height();
+ ctm.Scale(width / fb_size.width(), height / fb_size.height());
+ return ctm;
+}
+
+bool TouchTransformerController::ShouldComputeMirrorModeTouchTransformer(
+ const DisplayInfo& touch_display) const {
+ if (force_compute_mirror_mode_touch_transformer_)
+ return true;
+
+ if (touch_display.touch_device_id() == 0)
+ return false;
+
+ const ui::DisplayConfigurator::DisplayState* state = NULL;
+ const std::vector<ui::DisplayConfigurator::DisplayState>& cached_displays =
+ Shell::GetInstance()->display_configurator()->cached_displays();
+ for (size_t i = 0; i < cached_displays.size(); i++) {
+ if (cached_displays[i].touch_device_id == touch_display.touch_device_id()) {
+ state = &cached_displays[i];
+ break;
+ }
+ }
+
+ if (!state || state->mirror_mode == state->display->native_mode() ||
+ !state->display->is_aspect_preserving_scaling()) {
+ return false;
+ }
+ return true;
+}
+
+// This function computes the mirror mode TouchTransformer for |touch_display|.
+// When internal monitor is applied a resolution that does not have
+// the same aspect ratio as its native resolution, there would be
+// blank regions in the letterboxing/pillarboxing mode.
+// The TouchTransformer will make sure the touch events on the blank region
+// have negative coordinates and touch events within the chrome region
+// have the correct positive coordinates.
+gfx::Transform TouchTransformerController::GetMirrorModeTouchTransformer(
+ const DisplayInfo& touch_display) const {
+ gfx::Transform ctm;
+ if (!ShouldComputeMirrorModeTouchTransformer(touch_display))
+ return ctm;
+
+ float mirror_width = touch_display.bounds_in_native().width();
+ float mirror_height = touch_display.bounds_in_native().height();
+ float native_width = 0;
+ float native_height = 0;
+
+ std::vector<DisplayMode> modes = touch_display.display_modes();
+ for (size_t i = 0; i < modes.size(); i++) {
+ if (modes[i].native) {
+ native_width = modes[i].size.width();
+ native_height = modes[i].size.height();
+ break;
+ }
+ }
+
+ if (native_height == 0.0 || mirror_height == 0.0 ||
+ native_width == 0.0 || mirror_width == 0.0)
+ return ctm;
+
+ float native_ar = static_cast<float>(native_width) /
+ static_cast<float>(native_height);
+ float mirror_ar = static_cast<float>(mirror_width) /
+ static_cast<float>(mirror_height);
+
+ if (mirror_ar > native_ar) { // Letterboxing
+ // Translate before scale.
+ ctm.Translate(0.0, (1.0 - mirror_ar / native_ar) * 0.5 * mirror_height);
+ ctm.Scale(1.0, mirror_ar / native_ar);
+ return ctm;
+ }
+
+ if (native_ar > mirror_ar) { // Pillarboxing
+ // Translate before scale.
+ ctm.Translate((1.0 - native_ar / mirror_ar) * 0.5 * mirror_width, 0.0);
+ ctm.Scale(native_ar / mirror_ar, 1.0);
+ return ctm;
+ }
+
+ return ctm; // Same aspect ratio - return identity
+}
+
+TouchTransformerController::TouchTransformerController() :
+ force_compute_mirror_mode_touch_transformer_ (false) {
+ Shell::GetInstance()->display_controller()->AddObserver(this);
+}
+
+TouchTransformerController::~TouchTransformerController() {
+ Shell::GetInstance()->display_controller()->RemoveObserver(this);
+}
+
+void TouchTransformerController::UpdateTouchTransformer() const {
+ ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
+ device_manager->ClearTouchTransformerRecord();
+
+ // Display IDs and DisplayInfo for mirror or extended mode.
+ int64 display1_id = gfx::Display::kInvalidDisplayID;
+ int64 display2_id = gfx::Display::kInvalidDisplayID;
+ DisplayInfo display1;
+ DisplayInfo display2;
+ // Display ID and DisplayInfo for single display mode.
+ int64 single_display_id = gfx::Display::kInvalidDisplayID;
+ DisplayInfo single_display;
+
+ DisplayController* display_controller =
+ Shell::GetInstance()->display_controller();
+ ui::MultipleDisplayState display_state =
+ Shell::GetInstance()->display_configurator()->display_state();
+ if (display_state == ui::MULTIPLE_DISPLAY_STATE_INVALID ||
+ display_state == ui::MULTIPLE_DISPLAY_STATE_HEADLESS) {
+ return;
+ } else if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
+ display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) {
+ // TODO(miletus) : Handle DUAL_EXTENDED with software mirroring.
+ DisplayIdPair id_pair = GetDisplayManager()->GetCurrentDisplayIdPair();
+ display1_id = id_pair.first;
+ display2_id = id_pair.second;
+ DCHECK(display1_id != gfx::Display::kInvalidDisplayID &&
+ display2_id != gfx::Display::kInvalidDisplayID);
+ display1 = GetDisplayManager()->GetDisplayInfo(display1_id);
+ display2 = GetDisplayManager()->GetDisplayInfo(display2_id);
+ } else {
+ single_display_id = GetDisplayManager()->first_display_id();
+ DCHECK(single_display_id != gfx::Display::kInvalidDisplayID);
+ single_display = GetDisplayManager()->GetDisplayInfo(single_display_id);
+ }
+
+ if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) {
+ // In mirror mode, both displays share the same root window so
+ // both display ids are associated with the root window.
+ aura::Window* root = display_controller->GetPrimaryRootWindow();
+ RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID(
+ display1_id, display2_id);
+ device_manager->UpdateTouchInfoForDisplay(
+ display1_id,
+ display1.touch_device_id(),
+ GetMirrorModeTouchTransformer(display1));
+ device_manager->UpdateTouchInfoForDisplay(
+ display2_id,
+ display2.touch_device_id(),
+ GetMirrorModeTouchTransformer(display2));
+ return;
+ }
+
+ if (display_state == ui::MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) {
+ // In extended mode, each display is associated with one root window.
+ aura::Window* root1 =
+ display_controller->GetRootWindowForDisplayId(display1_id);
+ aura::Window* root2 =
+ display_controller->GetRootWindowForDisplayId(display2_id);
+ RootWindowController::ForWindow(root1)->ash_host()->UpdateDisplayID(
+ display1_id, gfx::Display::kInvalidDisplayID);
+ RootWindowController::ForWindow(root2)->ash_host()->UpdateDisplayID(
+ display2_id, gfx::Display::kInvalidDisplayID);
+ gfx::Size fb_size =
+ Shell::GetInstance()->display_configurator()->framebuffer_size();
+ device_manager->UpdateTouchInfoForDisplay(
+ display1_id,
+ display1.touch_device_id(),
+ GetExtendedModeTouchTransformer(display1, fb_size));
+ device_manager->UpdateTouchInfoForDisplay(
+ display2_id,
+ display2.touch_device_id(),
+ GetExtendedModeTouchTransformer(display2, fb_size));
+ return;
+ }
+
+ // Single display mode. The root window has one associated display id.
+ aura::Window* root =
+ display_controller->GetRootWindowForDisplayId(single_display.id());
+ RootWindowController::ForWindow(root)->ash_host()->UpdateDisplayID(
+ single_display.id(), gfx::Display::kInvalidDisplayID);
+ device_manager->UpdateTouchInfoForDisplay(single_display_id,
+ single_display.touch_device_id(),
+ gfx::Transform());
+}
+
+void TouchTransformerController::OnDisplaysInitialized() {
+ UpdateTouchTransformer();
+}
+
+void TouchTransformerController::OnDisplayConfigurationChanged() {
+ UpdateTouchTransformer();
+}
+
+} // namespace ash
diff --git a/ash/touch/touch_transformer_controller.h b/ash/touch/touch_transformer_controller.h
new file mode 100644
index 0000000..f06be29
--- /dev/null
+++ b/ash/touch/touch_transformer_controller.h
@@ -0,0 +1,55 @@
+// Copyright 2014 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.
+
+#ifndef ASH_TOUCH_TOUCH_TRANSFORMER_CONTROLLER_H_
+#define ASH_TOUCH_TOUCH_TRANSFORMER_CONTROLLER_H_
+
+#include "ash/ash_export.h"
+#include "ash/display/display_controller.h"
+#include "ui/gfx/transform.h"
+
+namespace ash {
+
+// TouchTransformerController listens to display configuration change
+// and updates the touch transformation for touch displays.
+class ASH_EXPORT TouchTransformerController
+ : public DisplayController::Observer {
+ public:
+ TouchTransformerController();
+ virtual ~TouchTransformerController();
+
+ // Updates the TouchTransformer for touch device and pushes the new
+ // TouchTransformer into device manager.
+ void UpdateTouchTransformer() const;
+
+ // DisplayController::Observer:
+ virtual void OnDisplaysInitialized() OVERRIDE;
+ virtual void OnDisplayConfigurationChanged() OVERRIDE;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(TouchTransformerControllerTest,
+ TouchTransformerMirrorModeLetterboxing);
+ FRIEND_TEST_ALL_PREFIXES(TouchTransformerControllerTest,
+ TouchTransformerMirrorModePillarboxing);
+ FRIEND_TEST_ALL_PREFIXES(TouchTransformerControllerTest,
+ TouchTransformerExtendedMode);
+
+ bool ShouldComputeMirrorModeTouchTransformer(
+ const DisplayInfo& touch_display) const ;
+
+ gfx::Transform GetMirrorModeTouchTransformer(
+ const DisplayInfo& touch_display) const;
+
+ gfx::Transform GetExtendedModeTouchTransformer(
+ const DisplayInfo& touch_display, const gfx::Size& fb_size) const;
+
+ // For unittests only.
+ bool force_compute_mirror_mode_touch_transformer_;
+
+ DISALLOW_COPY_AND_ASSIGN(TouchTransformerController);
+};
+
+} // namespace ash
+
+#endif // ASH_TOUCH_TOUCH_TRANSFORMER_CONTROLLER_H_
diff --git a/ash/touch/touch_transformer_controller_unittest.cc b/ash/touch/touch_transformer_controller_unittest.cc
new file mode 100644
index 0000000..1b8fa1f
--- /dev/null
+++ b/ash/touch/touch_transformer_controller_unittest.cc
@@ -0,0 +1,206 @@
+// Copyright 2014 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 "ash/touch/touch_transformer_controller.h"
+
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/events/x/device_data_manager.h"
+#include "ui/gfx/display.h"
+
+namespace ash {
+
+namespace {
+DisplayInfo CreateDisplayInfo(int64 id,
+ int touch_device_id,
+ const gfx::Rect& bounds) {
+ DisplayInfo info(id, std::string(), false);
+ info.SetBounds(bounds);
+ info.set_touch_device_id(touch_device_id);
+ return info;
+}
+} // namespace
+
+typedef test::AshTestBase TouchTransformerControllerTest;
+
+TEST_F(TouchTransformerControllerTest, TouchTransformerMirrorModeLetterboxing) {
+ // The internal display has native resolution of 2560x1700, and in
+ // mirror mode it is configured as 1920x1200. This is in letterboxing
+ // mode.
+ DisplayInfo internal_display_info =
+ CreateDisplayInfo(1, 10, gfx::Rect(0, 0, 1920, 1200));
+ std::vector<DisplayMode> internal_modes;
+ internal_modes.push_back(
+ DisplayMode(gfx::Size(2560, 1700), 60, false, true));
+ internal_modes.push_back(
+ DisplayMode(gfx::Size(1920, 1200), 60, false, false));
+ internal_display_info.set_display_modes(internal_modes);
+
+ DisplayInfo external_display_info =
+ CreateDisplayInfo(2, 11, gfx::Rect(0, 0, 1920, 1200));
+
+ TouchTransformerController* tt_controller =
+ Shell::GetInstance()->touch_transformer_controller();
+ ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
+
+ tt_controller->force_compute_mirror_mode_touch_transformer_ = true;
+ device_manager->UpdateTouchInfoForDisplay(
+ internal_display_info.id(),
+ internal_display_info.touch_device_id(),
+ tt_controller->GetMirrorModeTouchTransformer(internal_display_info));
+
+ tt_controller->force_compute_mirror_mode_touch_transformer_ = false;
+ device_manager->UpdateTouchInfoForDisplay(
+ external_display_info.id(),
+ external_display_info.touch_device_id(),
+ tt_controller->GetMirrorModeTouchTransformer(external_display_info));
+
+ EXPECT_EQ(1, device_manager->GetDisplayForTouchDevice(10));
+ EXPECT_EQ(2, device_manager->GetDisplayForTouchDevice(11));
+
+ // External touch display has the default TouchTransformer.
+ float x = 100.0;
+ float y = 100.0;
+ device_manager->ApplyTouchTransformer(11, &x, &y);
+ EXPECT_EQ(100, x);
+ EXPECT_EQ(100, y);
+
+ // In letterboxing, there is (1-2560*(1200/1920)/1700)/2 = 2.95% of the
+ // height on both the top & bottom region of the screen is blank.
+ // When touch events coming at Y range [0, 1200), the mapping should be
+ // [0, ~35] ---> < 0
+ // [~35, ~1165] ---> [0, 1200)
+ // [~1165, 1200] ---> >= 1200
+ x = 100.0;
+ y = 35.0;
+ device_manager->ApplyTouchTransformer(10, &x, &y);
+ EXPECT_EQ(100, static_cast<int>(x));
+ EXPECT_EQ(0, static_cast<int>(y));
+
+ x = 100.0;
+ y = 1165.0;
+ device_manager->ApplyTouchTransformer(10, &x, &y);
+ EXPECT_EQ(100, static_cast<int>(x));
+ EXPECT_EQ(1200, static_cast<int>(y));
+}
+
+TEST_F(TouchTransformerControllerTest, TouchTransformerMirrorModePillarboxing) {
+ // The internal display has native resolution of 1366x768, and in
+ // mirror mode it is configured as 1024x768. This is in pillarboxing
+ // mode.
+ DisplayInfo internal_display_info =
+ CreateDisplayInfo(1, 10, gfx::Rect(0, 0, 1024, 768));
+ std::vector<DisplayMode> internal_modes;
+ internal_modes.push_back(
+ DisplayMode(gfx::Size(1366, 768), 60, false, true));
+ internal_modes.push_back(
+ DisplayMode(gfx::Size(1024, 768), 60, false, false));
+ internal_display_info.set_display_modes(internal_modes);
+
+ DisplayInfo external_display_info =
+ CreateDisplayInfo(2, 11, gfx::Rect(0, 0, 1024, 768));
+
+ TouchTransformerController* tt_controller =
+ Shell::GetInstance()->touch_transformer_controller();
+ ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
+
+ tt_controller->force_compute_mirror_mode_touch_transformer_ = true;
+ device_manager->UpdateTouchInfoForDisplay(
+ internal_display_info.id(),
+ internal_display_info.touch_device_id(),
+ tt_controller->GetMirrorModeTouchTransformer(internal_display_info));
+
+ tt_controller->force_compute_mirror_mode_touch_transformer_ = false;
+ device_manager->UpdateTouchInfoForDisplay(
+ external_display_info.id(),
+ external_display_info.touch_device_id(),
+ tt_controller->GetMirrorModeTouchTransformer(external_display_info));
+
+ EXPECT_EQ(1, device_manager->GetDisplayForTouchDevice(10));
+ EXPECT_EQ(2, device_manager->GetDisplayForTouchDevice(11));
+
+ // External touch display has the default TouchTransformer.
+ float x = 100.0;
+ float y = 100.0;
+ device_manager->ApplyTouchTransformer(11, &x, &y);
+ EXPECT_EQ(100, x);
+ EXPECT_EQ(100, y);
+
+ // In pillarboxing, there is (1-768*(1024/768)/1366)/2 = 12.5% of the
+ // width on both the left & rigth region of the screen is blank.
+ // When touch events coming at X range [0, 1024), the mapping should be
+ // [0, ~128] ---> < 0
+ // [~128, ~896] ---> [0, 1024)
+ // [~896, 1024] ---> >= 1024
+ x = 128.0;
+ y = 100.0;
+ device_manager->ApplyTouchTransformer(10, &x, &y);
+ EXPECT_EQ(0, static_cast<int>(x));
+ EXPECT_EQ(100, static_cast<int>(y));
+
+ x = 896.0;
+ y = 100.0;
+ device_manager->ApplyTouchTransformer(10, &x, &y);
+ EXPECT_EQ(1024, static_cast<int>(x));
+ EXPECT_EQ(100, static_cast<int>(y));
+}
+
+TEST_F(TouchTransformerControllerTest, TouchTransformerExtendedMode) {
+ // The internal display has size 1366 x 768. The external display has
+ // size 2560x1600. The total frame buffer is 2560x2428,
+ // where 2428 = 768 + 60 (hidden gap) + 1600
+ // and the sceond monitor is translated to Point (0, 828) in the
+ // framebuffer.
+ DisplayInfo display1 = CreateDisplayInfo(1, 5, gfx::Rect(0, 0, 1366, 768));
+ DisplayInfo display2 = CreateDisplayInfo(2, 6, gfx::Rect(0, 828, 2560, 1600));
+ gfx::Size fb_size(2560, 2428);
+
+ TouchTransformerController* tt_controller =
+ Shell::GetInstance()->touch_transformer_controller();
+ ui::DeviceDataManager* device_manager = ui::DeviceDataManager::GetInstance();
+
+ device_manager->UpdateTouchInfoForDisplay(
+ display1.id(),
+ display1.touch_device_id(),
+ tt_controller->GetExtendedModeTouchTransformer(display1, fb_size));
+
+ device_manager->UpdateTouchInfoForDisplay(
+ display2.id(),
+ display2.touch_device_id(),
+ tt_controller->GetExtendedModeTouchTransformer(display2, fb_size));
+
+ EXPECT_EQ(1, device_manager->GetDisplayForTouchDevice(5));
+ EXPECT_EQ(2, device_manager->GetDisplayForTouchDevice(6));
+
+ // Mapping for touch events from internal touch display:
+ // [0, 2560) x [0, 2428) -> [0, 1366) x [0, 768)
+ float x = 0.0;
+ float y = 0.0;
+ device_manager->ApplyTouchTransformer(5, &x, &y);
+ EXPECT_EQ(0, static_cast<int>(x));
+ EXPECT_EQ(0, static_cast<int>(y));
+
+ x = 2559.0;
+ y = 2427.0;
+ device_manager->ApplyTouchTransformer(5, &x, &y);
+ EXPECT_EQ(1365, static_cast<int>(x));
+ EXPECT_EQ(767, static_cast<int>(y));
+
+ // Mapping for touch events from external touch display:
+ // [0, 2560) x [0, 2428) -> [0, 2560) x [0, 1600)
+ x = 0.0;
+ y = 0.0;
+ device_manager->ApplyTouchTransformer(6, &x, &y);
+ EXPECT_EQ(0, static_cast<int>(x));
+ EXPECT_EQ(0, static_cast<int>(y));
+
+ x = 2559.0;
+ y = 2427.0;
+ device_manager->ApplyTouchTransformer(6, &x, &y);
+ EXPECT_EQ(2559, static_cast<int>(x));
+ EXPECT_EQ(1599, static_cast<int>(y));
+}
+
+} // namespace ash