Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Side by Side Diff: ash/host/ash_window_tree_host_x11.cc

Issue 201573015: Introdcue AshWindowTreeHost and move ash/chrome specific code in WTH to ash. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/host/ash_window_tree_host_x11.h"
6
7 #include <strings.h>
8 #include <X11/cursorfont.h>
9 #include <X11/extensions/Xfixes.h>
10 #include <X11/extensions/XInput2.h>
11 #include <X11/extensions/Xrandr.h>
12 #include <X11/Xatom.h>
13 #include <X11/Xcursor/Xcursor.h>
14 #include <X11/Xlib.h>
15
16 #include <algorithm>
17 #include <limits>
18 #include <string>
19
20 #include "ash/host/root_window_transformer.h"
21 #include "base/basictypes.h"
22 #include "base/command_line.h"
23 #include "base/debug/trace_event.h"
24 #include "base/message_loop/message_loop.h"
25 #include "base/message_loop/message_pump_x11.h"
26 #include "base/stl_util.h"
27 #include "base/strings/string_number_conversions.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "base/sys_info.h"
31 #include "ui/gfx/size_conversions.h"
32 #include "ui/aura/client/cursor_client.h"
33 #include "ui/aura/client/screen_position_client.h"
34 #include "ui/aura/env.h"
35 #include "ui/aura/window.h"
36 #include "ui/aura/window_event_dispatcher.h"
37 #include "ui/base/cursor/cursor.h"
38 #include "ui/base/ui_base_switches.h"
39 #include "ui/base/view_prop.h"
40 #include "ui/base/x/x11_util.h"
41 #include "ui/compositor/compositor.h"
42 #include "ui/compositor/dip_util.h"
43 #include "ui/compositor/layer.h"
44 #include "ui/events/event.h"
45 #include "ui/events/event_switches.h"
46 #include "ui/events/event_utils.h"
47 #include "ui/events/keycodes/keyboard_codes.h"
48 #include "ui/events/x/device_data_manager.h"
49 #include "ui/events/x/device_list_cache_x.h"
50 #include "ui/events/x/touch_factory_x11.h"
51 #include "ui/gfx/screen.h"
52
53 using std::max;
54 using std::min;
55
56 namespace ash {
57
58 namespace internal {
59
60 // Accomplishes 2 tasks concerning touch event calibration:
61 // 1. Being a message-pump observer,
62 // routes all the touch events to the X root window,
63 // where they can be calibrated later.
64 // 2. Has the Calibrate method that does the actual bezel calibration,
65 // when invoked from X root window's event dispatcher.
66 class TouchEventCalibrate : public base::MessagePumpObserver {
67 public:
68 TouchEventCalibrate()
69 : left_(0),
70 right_(0),
71 top_(0),
72 bottom_(0) {
73 base::MessageLoopForUI::current()->AddObserver(this);
74 #if defined(USE_XI2_MT)
75 std::vector<std::string> parts;
76 if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
77 switches::kTouchCalibration), ",", &parts) >= 4) {
78 if (!base::StringToInt(parts[0], &left_))
79 DLOG(ERROR) << "Incorrect left border calibration value passed.";
80 if (!base::StringToInt(parts[1], &right_))
81 DLOG(ERROR) << "Incorrect right border calibration value passed.";
82 if (!base::StringToInt(parts[2], &top_))
83 DLOG(ERROR) << "Incorrect top border calibration value passed.";
84 if (!base::StringToInt(parts[3], &bottom_))
85 DLOG(ERROR) << "Incorrect bottom border calibration value passed.";
86 }
87 #endif // defined(USE_XI2_MT)
88 }
89
90 virtual ~TouchEventCalibrate() {
91 base::MessageLoopForUI::current()->RemoveObserver(this);
92 }
93
94 // Modify the location of the |event|,
95 // expanding it from |bounds| to (|bounds| + bezels).
96 // Required when touchscreen is bigger than screen (i.e. has bezels),
97 // because we receive events in touchscreen coordinates,
98 // which need to be expanded when converting to screen coordinates,
99 // so that location on bezels will be outside of screen area.
100 void Calibrate(ui::TouchEvent* event, const gfx::Rect& bounds) {
101 #if defined(USE_XI2_MT)
102 int x = event->x();
103 int y = event->y();
104
105 if (!left_ && !right_ && !top_ && !bottom_)
106 return;
107
108 const int resolution_x = bounds.width();
109 const int resolution_y = bounds.height();
110 // The "grace area" (10% in this case) is to make it easier for the user to
111 // navigate to the corner.
112 const double kGraceAreaFraction = 0.1;
113 if (left_ || right_) {
114 // Offset the x position to the real
115 x -= left_;
116 // Check if we are in the grace area of the left side.
117 // Note: We might not want to do this when the gesture is locked?
118 if (x < 0 && x > -left_ * kGraceAreaFraction)
119 x = 0;
120 // Check if we are in the grace area of the right side.
121 // Note: We might not want to do this when the gesture is locked?
122 if (x > resolution_x - left_ &&
123 x < resolution_x - left_ + right_ * kGraceAreaFraction)
124 x = resolution_x - left_;
125 // Scale the screen area back to the full resolution of the screen.
126 x = (x * resolution_x) / (resolution_x - (right_ + left_));
127 }
128 if (top_ || bottom_) {
129 // When there is a top bezel we add our border,
130 y -= top_;
131
132 // Check if we are in the grace area of the top side.
133 // Note: We might not want to do this when the gesture is locked?
134 if (y < 0 && y > -top_ * kGraceAreaFraction)
135 y = 0;
136
137 // Check if we are in the grace area of the bottom side.
138 // Note: We might not want to do this when the gesture is locked?
139 if (y > resolution_y - top_ &&
140 y < resolution_y - top_ + bottom_ * kGraceAreaFraction)
141 y = resolution_y - top_;
142 // Scale the screen area back to the full resolution of the screen.
143 y = (y * resolution_y) / (resolution_y - (bottom_ + top_));
144 }
145
146 // Set the modified coordinate back to the event.
147 if (event->root_location() == event->location()) {
148 // Usually those will be equal,
149 // if not, I am not sure what the correct value should be.
150 event->set_root_location(gfx::Point(x, y));
151 }
152 event->set_location(gfx::Point(x, y));
153 #endif // defined(USE_XI2_MT)
154 }
155
156 private:
157 // Overridden from base::MessagePumpObserver:
158 virtual base::EventStatus WillProcessEvent(
159 const base::NativeEvent& event) OVERRIDE {
160 #if defined(USE_XI2_MT)
161 if (event->type == GenericEvent &&
162 (event->xgeneric.evtype == XI_TouchBegin ||
163 event->xgeneric.evtype == XI_TouchUpdate ||
164 event->xgeneric.evtype == XI_TouchEnd)) {
165 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(event->xcookie.data);
166 xievent->event = xievent->root;
167 xievent->event_x = xievent->root_x;
168 xievent->event_y = xievent->root_y;
169 }
170 #endif // defined(USE_XI2_MT)
171 return base::EVENT_CONTINUE;
172 }
173
174 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
175 }
176
177 // The difference in screen's native resolution pixels between
178 // the border of the touchscreen and the border of the screen,
179 // aka bezel sizes.
180 int left_;
181 int right_;
182 int top_;
183 int bottom_;
184
185 DISALLOW_COPY_AND_ASSIGN(TouchEventCalibrate);
186 };
187
188 } // namespace internal
189
190 namespace {
191 class SimpleRootWindowTransformer : public RootWindowTransformer {
192 public:
193 SimpleRootWindowTransformer(const aura::Window* root_window,
194 const gfx::Transform& transform)
195 : root_window_(root_window),
196 transform_(transform) {
197 }
198
199 // RootWindowTransformer overrides:
200 virtual gfx::Transform GetTransform() const OVERRIDE {
201 return transform_;
202 }
203
204 virtual gfx::Transform GetInverseTransform() const OVERRIDE {
205 gfx::Transform invert;
206 if (!transform_.GetInverse(&invert))
207 return transform_;
208 return invert;
209 }
210
211 virtual gfx::Rect GetRootWindowBounds(
212 const gfx::Size& host_size) const OVERRIDE {
213 gfx::Rect bounds(host_size);
214 gfx::RectF new_bounds(ui::ConvertRectToDIP(root_window_->layer(), bounds));
215 transform_.TransformRect(&new_bounds);
216 return gfx::Rect(gfx::ToFlooredSize(new_bounds.size()));
217 }
218
219 virtual gfx::Insets GetHostInsets() const OVERRIDE {
220 return gfx::Insets();
221 }
222
223 private:
224 virtual ~SimpleRootWindowTransformer() {}
225
226 const aura::Window* root_window_;
227 const gfx::Transform transform_;
228
229 DISALLOW_COPY_AND_ASSIGN(SimpleRootWindowTransformer);
230 };
231
232 }
233
234 ////////////////////////////////////////////////////////////////////////////////
235 // AshWindowTreeHostX11
236
237 AshWindowTreeHostX11::AshWindowTreeHostX11(const gfx::Rect& bounds)
238 : WindowTreeHostX11(bounds),
239 is_internal_display_(false),
240 touch_calibrate_(new internal::TouchEventCalibrate) {
241 aura::Env::GetInstance()->AddObserver(this);
242 transformer_.reset(
243 new SimpleRootWindowTransformer(window(), gfx::Transform()));
244 }
245
246 AshWindowTreeHostX11::~AshWindowTreeHostX11() {
247 aura::Env::GetInstance()->RemoveObserver(this);
248 UnConfineCursor();
249 }
250
251 void AshWindowTreeHostX11::ToggleFullScreen() {
252 NOTIMPLEMENTED();
253 }
254
255 gfx::Insets AshWindowTreeHostX11::GetInsets() const {
256 return insets_;
257 }
258
259 void AshWindowTreeHostX11::SetInsets(const gfx::Insets& insets) {
260 insets_ = insets;
261 if (pointer_barriers_) {
262 UnConfineCursor();
263 ConfineCursorToRootWindow();
264 }
265 }
266
267 bool AshWindowTreeHostX11::ConfineCursorToRootWindow() {
268 #if XFIXES_MAJOR >= 5
269 DCHECK(!pointer_barriers_.get());
270 if (pointer_barriers_)
271 return false;
272 pointer_barriers_.reset(new XID[4]);
273 gfx::Rect barrier_rect(bounds());
274 barrier_rect.Inset(insets_);
275 // Horizontal, top barriers.
276 pointer_barriers_[0] = XFixesCreatePointerBarrier(
277 xdisplay(), x_root_window(),
278 barrier_rect.x(), barrier_rect.y(), barrier_rect.right(), barrier_rect.y() ,
279 BarrierPositiveY,
280 0, XIAllDevices);
281 // Horizontal, bottom barriers.
282 pointer_barriers_[1] = XFixesCreatePointerBarrier(
283 xdisplay(), x_root_window(),
284 barrier_rect.x(), barrier_rect.bottom(), barrier_rect.right(), barrier_re ct.bottom(),
285 BarrierNegativeY,
286 0, XIAllDevices);
287 // Vertical, left barriers.
288 pointer_barriers_[2] = XFixesCreatePointerBarrier(
289 xdisplay(), x_root_window(),
290 barrier_rect.x(), barrier_rect.y(), barrier_rect.x(), barrier_rect.bottom( ),
291 BarrierPositiveX,
292 0, XIAllDevices);
293 // Vertical, right barriers.
294 pointer_barriers_[3] = XFixesCreatePointerBarrier(
295 xdisplay(), x_root_window(),
296 barrier_rect.right(), barrier_rect.y(), barrier_rect.right(), barrier_rect .bottom(),
297 BarrierNegativeX,
298 0, XIAllDevices);
299 #endif
300 return true;
301 }
302
303 void AshWindowTreeHostX11::UnConfineCursor() {
304 #if XFIXES_MAJOR >= 5
305 if (pointer_barriers_) {
306 XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[0]);
307 XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[1]);
308 XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[2]);
309 XFixesDestroyPointerBarrier(xdisplay(), pointer_barriers_[3]);
310 pointer_barriers_.reset();
311 }
312 #endif
313 }
314
315 void AshWindowTreeHostX11::SetRootWindowTransformer(
316 scoped_ptr<RootWindowTransformer> transformer) {
317 transformer_ = transformer.Pass();
318 SetInsets(transformer_->GetHostInsets());
319 window()->SetTransform(transformer_->GetTransform());
320 // If the layer is not animating, then we need to update the root window
321 // size immediately.
322 if (!window()->layer()->GetAnimator()->is_animating())
323 UpdateRootWindowSize(GetBounds().size());
324 }
325
326 gfx::Transform AshWindowTreeHostX11::GetRootTransform() const {
327 float scale = ui::GetDeviceScaleFactor(window()->layer());
328 gfx::Transform transform;
329 transform.Scale(scale, scale);
330 transform *= transformer_->GetTransform();
331 return transform;
332 }
333
334 aura::WindowTreeHost* AshWindowTreeHostX11::AsWindowTreeHost() {
335 return this;
336 }
337
338 void AshWindowTreeHostX11::SetTransform(const gfx::Transform& transform) {
339 scoped_ptr<RootWindowTransformer> transformer(
340 new SimpleRootWindowTransformer(window(), transform));
341 SetRootWindowTransformer(transformer.Pass());
342 }
343
344 gfx::Transform AshWindowTreeHostX11::GetInverseRootTransform() const {
345 float scale = ui::GetDeviceScaleFactor(window()->layer());
346 gfx::Transform transform;
347 transform.Scale(1.0f / scale, 1.0f / scale);
348 return transformer_->GetInverseTransform() * transform;
349 }
350
351 void AshWindowTreeHostX11::UpdateRootWindowSize(const gfx::Size& host_size) {
352 window()->SetBounds(transformer_->GetRootWindowBounds(host_size));
353 }
354
355 void AshWindowTreeHostX11::OnCursorVisibilityChangedNative(bool show) {
356 SetCrOSTapPaused(!show);
357 }
358
359 void AshWindowTreeHostX11::OnWindowInitialized(aura::Window* window) {
360 }
361
362 void AshWindowTreeHostX11::OnHostInitialized(aura::WindowTreeHost* host) {
363 // TODO(beng): I'm not sure that this comment makes much sense anymore??
364 // UpdateIsInternalDisplay relies on WED's kDisplayIdKey property being set
365 // available by the time WED::Init is called. (set in
366 // DisplayManager::CreateRootWindowForDisplay)
367 // Ready when NotifyHostInitialized is called from WED::Init.
368
369 if (host != this)
370 return;
371 UpdateIsInternalDisplay();
372
373 // We have to enable Tap-to-click by default because the cursor is set to
374 // visible in Shell::InitRootWindowController.
375 SetCrOSTapPaused(false);
376 }
377
378
379 void AshWindowTreeHostX11::TranslateAndDispatchLocatedEvent(
380 ui::LocatedEvent* event) {
381 switch (event->type()) {
382 case ui::ET_TOUCH_MOVED:
383 case ui::ET_TOUCH_PRESSED:
384 case ui::ET_TOUCH_CANCELLED:
385 case ui::ET_TOUCH_RELEASED: {
386 // Bail out early before generating a ui::TouchEvent if this event
387 // is not within the range of this RootWindow. Converting an xevent
388 // to ui::TouchEvent might change the state of the global touch tracking
389 // state, e.g. touch release event can remove the touch id from the
390 // record, and doing this multiple time when there are multiple
391 // RootWindow will cause problem. So only generate the ui::TouchEvent
392 // when we are sure it belongs to this RootWindow.
393 if (base::SysInfo::IsRunningOnChromeOS() &&
394 !bounds().Contains(event->location()))
395 break;
396 ui::TouchEvent* touchev = static_cast<ui::TouchEvent*>(event);
397 if (base::SysInfo::IsRunningOnChromeOS()) {
398 // X maps the touch-surface to the size of the X root-window.
399 // In multi-monitor setup, Coordinate Transformation Matrix
400 // repositions the touch-surface onto part of X root-window
401 // containing aura root-window corresponding to the touchscreen.
402 // However, if aura root-window has non-zero origin,
403 // we need to relocate the event into aura root-window coordinates.
404 touchev->Relocate(bounds().origin());
405 #if defined(USE_XI2_MT)
406 if (is_internal_display_)
407 touch_calibrate_->Calibrate(touchev, bounds());
408 #endif // defined(USE_XI2_MT)
409 }
410 break;
411 }
412 default: {
413 aura::Window* root_window = window();
414 aura::client::ScreenPositionClient* screen_position_client =
415 aura::client::GetScreenPositionClient(root_window);
416 gfx::Rect local(bounds().size());
417
418 if (screen_position_client && !local.Contains(event->location())) {
419 gfx::Point location(event->location());
420 // In order to get the correct point in screen coordinates
421 // during passive grab, we first need to find on which host window
422 // the mouse is on, and find out the screen coordinates on that
423 // host window, then convert it back to this host window's coordinate.
424 screen_position_client->ConvertHostPointToScreen(root_window,
425 &location);
426 screen_position_client->ConvertPointFromScreen(root_window, &location);
427 ConvertPointToHost(&location);
428 event->set_location(location);
429 event->set_root_location(location);
430 }
431 break;
432 }
433 }
434 SendEventToProcessor(event);
435 }
436
437 void AshWindowTreeHostX11::UpdateIsInternalDisplay() {
438 aura::Window* root_window = window();
439 gfx::Screen* screen = gfx::Screen::GetScreenFor(root_window);
440 gfx::Display display = screen->GetDisplayNearestWindow(root_window);
441 is_internal_display_ = display.IsInternal();
442 }
443
444 void AshWindowTreeHostX11::SetCrOSTapPaused(bool state) {
445 if (!ui::IsXInput2Available())
446 return;
447 // Temporarily pause tap-to-click when the cursor is hidden.
448 Atom prop = atom_cache()->GetAtom("Tap Paused");
449 unsigned char value = state;
450 XIDeviceList dev_list =
451 ui::DeviceListCacheX::GetInstance()->GetXI2DeviceList(xdisplay());
452
453 // Only slave pointer devices could possibly have tap-paused property.
454 for (int i = 0; i < dev_list.count; i++) {
455 if (dev_list[i].use == XISlavePointer) {
456 Atom old_type;
457 int old_format;
458 unsigned long old_nvalues, bytes;
459 unsigned char* data;
460 int result = XIGetProperty(xdisplay(), dev_list[i].deviceid, prop, 0, 0,
461 False, AnyPropertyType, &old_type, &old_format,
462 &old_nvalues, &bytes, &data);
463 if (result != Success)
464 continue;
465 XFree(data);
466 XIChangeProperty(xdisplay(), dev_list[i].deviceid, prop, XA_INTEGER, 8,
467 PropModeReplace, &value, 1);
468 }
469 }
470 }
471
472
473 AshWindowTreeHost* AshWindowTreeHost::Create(const gfx::Rect& initial_bounds) {
474 return new AshWindowTreeHostX11(initial_bounds);
475 }
476
477 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698