1 // Copyright (c) 2012 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.
5 #include "ui/aura/root_window_host_x11.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>
20 #include "base/basictypes.h"
21 #include "base/command_line.h"
22 #include "base/debug/trace_event.h"
23 #include "base/message_loop/message_loop.h"
24 #include "base/message_loop/message_pump_x11.h"
25 #include "base/stl_util.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/sys_info.h"
30 #include "ui/aura/client/cursor_client.h"
31 #include "ui/aura/client/screen_position_client.h"
32 #include "ui/aura/client/user_action_client.h"
33 #include "ui/aura/env.h"
34 #include "ui/aura/root_window.h"
35 #include "ui/base/cursor/cursor.h"
36 #include "ui/base/ui_base_switches.h"
37 #include "ui/base/view_prop.h"
38 #include "ui/base/x/x11_util.h"
39 #include "ui/compositor/dip_util.h"
40 #include "ui/compositor/layer.h"
41 #include "ui/events/event.h"
42 #include "ui/events/event_utils.h"
43 #include "ui/events/keycodes/keyboard_codes.h"
44 #include "ui/events/x/device_data_manager.h"
45 #include "ui/events/x/device_list_cache_x.h"
46 #include "ui/events/x/touch_factory_x11.h"
47 #include "ui/gfx/screen.h"
56 // Standard Linux mouse buttons for going back and forward.
57 const int kBackMouseButton
= 8;
58 const int kForwardMouseButton
= 9;
60 const char* kAtomsToCache
[] = {
65 #if defined(OS_CHROMEOS)
66 "Tap Paused", // Defined in the gestures library.
71 ::Window
FindEventTarget(const base::NativeEvent
& xev
) {
72 ::Window target
= xev
->xany
.window
;
73 if (xev
->type
== GenericEvent
)
74 target
= static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
)->event
;
78 #if defined(USE_XI2_MT)
79 bool IsSideBezelsEnabled() {
80 static bool side_bezels_enabled
=
81 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
82 switches::kTouchSideBezels
) != "0";
83 return side_bezels_enabled
;
87 void SelectEventsForRootWindow() {
88 XDisplay
* display
= gfx::GetXDisplay();
89 ::Window root_window
= ui::GetX11RootWindow();
91 // Receive resize events for the root-window so |x_root_bounds_| can be
93 XWindowAttributes attr
;
94 XGetWindowAttributes(display
, root_window
, &attr
);
95 if (!(attr
.your_event_mask
& StructureNotifyMask
)) {
96 XSelectInput(display
, root_window
,
97 StructureNotifyMask
| attr
.your_event_mask
);
100 if (!ui::IsXInput2Available())
103 unsigned char mask
[XIMaskLen(XI_LASTEVENT
)] = {};
104 memset(mask
, 0, sizeof(mask
));
106 XISetMask(mask
, XI_HierarchyChanged
);
107 XISetMask(mask
, XI_KeyPress
);
108 XISetMask(mask
, XI_KeyRelease
);
111 evmask
.deviceid
= XIAllDevices
;
112 evmask
.mask_len
= sizeof(mask
);
114 XISelectEvents(display
, root_window
, &evmask
, 1);
116 // Selecting for touch events seems to fail on some cases (e.g. when logging
117 // in incognito). So select for non-touch events first, and then select for
118 // touch-events (but keep the other events in the mask, i.e. do not memset
119 // |mask| back to 0).
120 // TODO(sad): Figure out why this happens. http://crbug.com/153976
121 #if defined(USE_XI2_MT)
122 XISetMask(mask
, XI_TouchBegin
);
123 XISetMask(mask
, XI_TouchUpdate
);
124 XISetMask(mask
, XI_TouchEnd
);
125 XISelectEvents(display
, root_window
, &evmask
, 1);
129 bool default_override_redirect
= false;
135 // Accomplishes 2 tasks concerning touch event calibration:
136 // 1. Being a message-pump observer,
137 // routes all the touch events to the X root window,
138 // where they can be calibrated later.
139 // 2. Has the Calibrate method that does the actual bezel calibration,
140 // when invoked from X root window's event dispatcher.
141 class TouchEventCalibrate
: public base::MessagePumpObserver
{
143 TouchEventCalibrate()
148 base::MessageLoopForUI::current()->AddObserver(this);
149 #if defined(USE_XI2_MT)
150 std::vector
<std::string
> parts
;
151 if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
152 switches::kTouchCalibration
), ",", &parts
) >= 4) {
153 if (!base::StringToInt(parts
[0], &left_
))
154 DLOG(ERROR
) << "Incorrect left border calibration value passed.";
155 if (!base::StringToInt(parts
[1], &right_
))
156 DLOG(ERROR
) << "Incorrect right border calibration value passed.";
157 if (!base::StringToInt(parts
[2], &top_
))
158 DLOG(ERROR
) << "Incorrect top border calibration value passed.";
159 if (!base::StringToInt(parts
[3], &bottom_
))
160 DLOG(ERROR
) << "Incorrect bottom border calibration value passed.";
162 #endif // defined(USE_XI2_MT)
165 virtual ~TouchEventCalibrate() {
166 base::MessageLoopForUI::current()->RemoveObserver(this);
169 #if defined(USE_XI2_MT)
170 bool IsEventOnSideBezels(
171 const base::NativeEvent
& xev
,
172 const gfx::Rect
& bounds
) {
173 if (!left_
&& !right_
)
176 gfx::Point location
= ui::EventLocationFromNative(xev
);
177 int x
= location
.x();
178 return x
< left_
|| x
> bounds
.width() - right_
;
180 #endif // defined(USE_XI2_MT)
182 // Modify the location of the |event|,
183 // expanding it from |bounds| to (|bounds| + bezels).
184 // Required when touchscreen is bigger than screen (i.e. has bezels),
185 // because we receive events in touchscreen coordinates,
186 // which need to be expanded when converting to screen coordinates,
187 // so that location on bezels will be outside of screen area.
188 void Calibrate(ui::TouchEvent
* event
, const gfx::Rect
& bounds
) {
189 #if defined(USE_XI2_MT)
193 if (!left_
&& !right_
&& !top_
&& !bottom_
)
196 const int resolution_x
= bounds
.width();
197 const int resolution_y
= bounds
.height();
198 // The "grace area" (10% in this case) is to make it easier for the user to
199 // navigate to the corner.
200 const double kGraceAreaFraction
= 0.1;
201 if (left_
|| right_
) {
202 // Offset the x position to the real
204 // Check if we are in the grace area of the left side.
205 // Note: We might not want to do this when the gesture is locked?
206 if (x
< 0 && x
> -left_
* kGraceAreaFraction
)
208 // Check if we are in the grace area of the right side.
209 // Note: We might not want to do this when the gesture is locked?
210 if (x
> resolution_x
- left_
&&
211 x
< resolution_x
- left_
+ right_
* kGraceAreaFraction
)
212 x
= resolution_x
- left_
;
213 // Scale the screen area back to the full resolution of the screen.
214 x
= (x
* resolution_x
) / (resolution_x
- (right_
+ left_
));
216 if (top_
|| bottom_
) {
217 // When there is a top bezel we add our border,
220 // Check if we are in the grace area of the top side.
221 // Note: We might not want to do this when the gesture is locked?
222 if (y
< 0 && y
> -top_
* kGraceAreaFraction
)
225 // Check if we are in the grace area of the bottom side.
226 // Note: We might not want to do this when the gesture is locked?
227 if (y
> resolution_y
- top_
&&
228 y
< resolution_y
- top_
+ bottom_
* kGraceAreaFraction
)
229 y
= resolution_y
- top_
;
230 // Scale the screen area back to the full resolution of the screen.
231 y
= (y
* resolution_y
) / (resolution_y
- (bottom_
+ top_
));
234 // Set the modified coordinate back to the event.
235 if (event
->root_location() == event
->location()) {
236 // Usually those will be equal,
237 // if not, I am not sure what the correct value should be.
238 event
->set_root_location(gfx::Point(x
, y
));
240 event
->set_location(gfx::Point(x
, y
));
241 #endif // defined(USE_XI2_MT)
245 // Overridden from base::MessagePumpObserver:
246 virtual base::EventStatus
WillProcessEvent(
247 const base::NativeEvent
& event
) OVERRIDE
{
248 #if defined(USE_XI2_MT)
249 if (event
->type
== GenericEvent
&&
250 (event
->xgeneric
.evtype
== XI_TouchBegin
||
251 event
->xgeneric
.evtype
== XI_TouchUpdate
||
252 event
->xgeneric
.evtype
== XI_TouchEnd
)) {
253 XIDeviceEvent
* xievent
= static_cast<XIDeviceEvent
*>(event
->xcookie
.data
);
254 xievent
->event
= xievent
->root
;
255 xievent
->event_x
= xievent
->root_x
;
256 xievent
->event_y
= xievent
->root_y
;
258 #endif // defined(USE_XI2_MT)
259 return base::EVENT_CONTINUE
;
262 virtual void DidProcessEvent(const base::NativeEvent
& event
) OVERRIDE
{
265 // The difference in screen's native resolution pixels between
266 // the border of the touchscreen and the border of the screen,
273 DISALLOW_COPY_AND_ASSIGN(TouchEventCalibrate
);
276 } // namespace internal
278 ////////////////////////////////////////////////////////////////////////////////
279 // RootWindowHostX11::MouseMoveFilter filters out the move events that
280 // jump back and forth between two points. This happens when sub pixel mouse
281 // move is enabled and mouse move events could be jumping between two neighbor
282 // pixels, e.g. move(0,0), move(1,0), move(0,0), move(1,0) and on and on.
283 // The filtering is done by keeping track of the last two event locations and
284 // provides a Filter method to find out whether a mouse event is in a different
285 // location and should be processed.
287 class RootWindowHostX11::MouseMoveFilter
{
289 MouseMoveFilter() : insert_index_(0) {
290 for (size_t i
= 0; i
< kMaxEvents
; ++i
) {
291 const int int_max
= std::numeric_limits
<int>::max();
292 recent_locations_
[i
] = gfx::Point(int_max
, int_max
);
295 ~MouseMoveFilter() {}
297 // Returns true if |event| is known and should be ignored.
298 bool Filter(const base::NativeEvent
& event
) {
299 const gfx::Point
& location
= ui::EventLocationFromNative(event
);
300 for (size_t i
= 0; i
< kMaxEvents
; ++i
) {
301 if (location
== recent_locations_
[i
])
305 recent_locations_
[insert_index_
] = location
;
306 insert_index_
= (insert_index_
+ 1) % kMaxEvents
;
311 static const size_t kMaxEvents
= 2;
313 gfx::Point recent_locations_
[kMaxEvents
];
314 size_t insert_index_
;
316 DISALLOW_COPY_AND_ASSIGN(MouseMoveFilter
);
319 ////////////////////////////////////////////////////////////////////////////////
322 RootWindowHostX11::RootWindowHostX11(const gfx::Rect
& bounds
)
323 : xdisplay_(gfx::GetXDisplay()),
325 x_root_window_(DefaultRootWindow(xdisplay_
)),
326 current_cursor_(ui::kCursorNull
),
327 window_mapped_(false),
329 is_internal_display_(false),
330 focus_when_shown_(false),
331 touch_calibrate_(new internal::TouchEventCalibrate
),
332 mouse_move_filter_(new MouseMoveFilter
),
333 atom_cache_(xdisplay_
, kAtomsToCache
),
334 bezel_tracking_ids_(0) {
335 XSetWindowAttributes swa
;
336 memset(&swa
, 0, sizeof(swa
));
337 swa
.background_pixmap
= None
;
338 swa
.override_redirect
= default_override_redirect
;
339 xwindow_
= XCreateWindow(
340 xdisplay_
, x_root_window_
,
341 bounds
.x(), bounds
.y(), bounds
.width(), bounds
.height(),
343 CopyFromParent
, // depth
345 CopyFromParent
, // visual
346 CWBackPixmap
| CWOverrideRedirect
,
348 base::MessagePumpX11::Current()->AddDispatcherForWindow(this, xwindow_
);
349 base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this);
351 long event_mask
= ButtonPressMask
| ButtonReleaseMask
| FocusChangeMask
|
352 KeyPressMask
| KeyReleaseMask
|
353 EnterWindowMask
| LeaveWindowMask
|
354 ExposureMask
| VisibilityChangeMask
|
355 StructureNotifyMask
| PropertyChangeMask
|
357 XSelectInput(xdisplay_
, xwindow_
, event_mask
);
360 if (ui::IsXInput2Available())
361 ui::TouchFactory::GetInstance()->SetupXI2ForXWindow(xwindow_
);
363 SelectEventsForRootWindow();
365 // Get the initial size of the X root window.
366 XWindowAttributes attrs
;
367 XGetWindowAttributes(xdisplay_
, x_root_window_
, &attrs
);
368 x_root_bounds_
.SetRect(attrs
.x
, attrs
.y
, attrs
.width
, attrs
.height
);
370 // TODO(erg): We currently only request window deletion events. We also
371 // should listen for activation events and anything else that GTK+ listens
372 // for, and do something useful.
374 protocols
[0] = atom_cache_
.GetAtom("WM_DELETE_WINDOW");
375 protocols
[1] = atom_cache_
.GetAtom("_NET_WM_PING");
376 XSetWMProtocols(xdisplay_
, xwindow_
, protocols
, 2);
378 // We need a WM_CLIENT_MACHINE and WM_LOCALE_NAME value so we integrate with
379 // the desktop environment.
380 XSetWMProperties(xdisplay_
, xwindow_
, NULL
, NULL
, NULL
, 0, NULL
, NULL
, NULL
);
382 // Likewise, the X server needs to know this window's pid so it knows which
383 // program to kill if the window hangs.
384 // XChangeProperty() expects "pid" to be long.
385 COMPILE_ASSERT(sizeof(long) >= sizeof(pid_t
), pid_t_bigger_than_long
);
387 XChangeProperty(xdisplay_
,
389 atom_cache_
.GetAtom("_NET_WM_PID"),
393 reinterpret_cast<unsigned char*>(&pid
), 1);
395 XRRSelectInput(xdisplay_
, x_root_window_
,
396 RRScreenChangeNotifyMask
| RROutputChangeNotifyMask
);
397 Env::GetInstance()->AddObserver(this);
400 RootWindowHostX11::~RootWindowHostX11() {
401 Env::GetInstance()->RemoveObserver(this);
402 base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
403 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(xwindow_
);
407 XDestroyWindow(xdisplay_
, xwindow_
);
410 bool RootWindowHostX11::Dispatch(const base::NativeEvent
& event
) {
413 if (FindEventTarget(event
) == x_root_window_
)
414 return DispatchEventForRootWindow(event
);
418 aura::Window
* root_window
= GetRootWindow()->window();
419 client::CursorClient
* cursor_client
=
420 client::GetCursorClient(root_window
);
422 const gfx::Display display
= gfx::Screen::GetScreenFor(root_window
)->
423 GetDisplayNearestWindow(root_window
);
424 cursor_client
->SetDisplay(display
);
426 ui::MouseEvent
mouse_event(xev
);
427 // EnterNotify creates ET_MOUSE_MOVE. Mark as synthesized as this is not
428 // real mouse move event.
429 mouse_event
.set_flags(mouse_event
.flags() | ui::EF_IS_SYNTHESIZED
);
430 TranslateAndDispatchMouseEvent(&mouse_event
);
434 ui::MouseEvent
mouse_event(xev
);
435 TranslateAndDispatchMouseEvent(&mouse_event
);
439 gfx::Rect
damage_rect(xev
->xexpose
.x
, xev
->xexpose
.y
,
440 xev
->xexpose
.width
, xev
->xexpose
.height
);
441 delegate_
->AsRootWindow()->ScheduleRedrawRect(damage_rect
);
445 ui::KeyEvent
keydown_event(xev
, false);
446 delegate_
->OnHostKeyEvent(&keydown_event
);
450 ui::KeyEvent
keyup_event(xev
, false);
451 delegate_
->OnHostKeyEvent(&keyup_event
);
455 if (static_cast<int>(xev
->xbutton
.button
) == kBackMouseButton
||
456 static_cast<int>(xev
->xbutton
.button
) == kForwardMouseButton
) {
457 client::UserActionClient
* gesture_client
=
458 client::GetUserActionClient(delegate_
->AsRootWindow()->window());
459 if (gesture_client
) {
460 gesture_client
->OnUserAction(
461 static_cast<int>(xev
->xbutton
.button
) == kBackMouseButton
?
462 client::UserActionClient::BACK
:
463 client::UserActionClient::FORWARD
);
468 case ButtonRelease
: {
469 switch (ui::EventTypeFromNative(xev
)) {
470 case ui::ET_MOUSEWHEEL
: {
471 ui::MouseWheelEvent
mouseev(xev
);
472 TranslateAndDispatchMouseEvent(&mouseev
);
475 case ui::ET_MOUSE_PRESSED
:
476 case ui::ET_MOUSE_RELEASED
: {
477 ui::MouseEvent
mouseev(xev
);
478 TranslateAndDispatchMouseEvent(&mouseev
);
482 // No event is created for X11-release events for mouse-wheel buttons.
490 if (xev
->xfocus
.mode
!= NotifyGrab
)
491 delegate_
->OnHostLostWindowCapture();
493 case ConfigureNotify
: {
494 DCHECK_EQ(xwindow_
, xev
->xconfigure
.event
);
495 DCHECK_EQ(xwindow_
, xev
->xconfigure
.window
);
496 // It's possible that the X window may be resized by some other means
497 // than from within aura (e.g. the X window manager can change the
498 // size). Make sure the root window size is maintained properly.
499 gfx::Rect
bounds(xev
->xconfigure
.x
, xev
->xconfigure
.y
,
500 xev
->xconfigure
.width
, xev
->xconfigure
.height
);
501 bool size_changed
= bounds_
.size() != bounds
.size();
502 bool origin_changed
= bounds_
.origin() != bounds
.origin();
504 UpdateIsInternalDisplay();
505 // Always update barrier and mouse location because |bounds_| might
506 // have already been updated in |SetBounds|.
507 if (pointer_barriers_
) {
509 ConfineCursorToRootWindow();
512 delegate_
->OnHostResized(bounds
.size());
514 delegate_
->OnHostMoved(bounds_
.origin());
518 DispatchXI2Event(event
);
521 // If there's no window manager running, we need to assign the X input
522 // focus to our host window.
523 if (!IsWindowManagerPresent() && focus_when_shown_
)
524 XSetInputFocus(xdisplay_
, xwindow_
, RevertToNone
, CurrentTime
);
527 case ClientMessage
: {
528 Atom message_type
= static_cast<Atom
>(xev
->xclient
.data
.l
[0]);
529 if (message_type
== atom_cache_
.GetAtom("WM_DELETE_WINDOW")) {
530 // We have received a close message from the window manager.
531 delegate_
->AsRootWindow()->OnRootWindowHostCloseRequested();
532 } else if (message_type
== atom_cache_
.GetAtom("_NET_WM_PING")) {
533 XEvent reply_event
= *xev
;
534 reply_event
.xclient
.window
= x_root_window_
;
536 XSendEvent(xdisplay_
,
537 reply_event
.xclient
.window
,
539 SubstructureRedirectMask
| SubstructureNotifyMask
,
544 case MappingNotify
: {
545 switch (xev
->xmapping
.request
) {
546 case MappingModifier
:
547 case MappingKeyboard
:
548 XRefreshKeyboardMapping(&xev
->xmapping
);
549 delegate_
->AsRootWindow()->OnKeyboardMappingChanged();
552 ui::DeviceDataManager::GetInstance()->UpdateButtonMap();
555 NOTIMPLEMENTED() << " Unknown request: " << xev
->xmapping
.request
;
561 // Discard all but the most recent motion event that targets the same
562 // window with unchanged state.
564 while (XPending(xev
->xany
.display
)) {
566 XPeekEvent(xev
->xany
.display
, &next_event
);
567 if (next_event
.type
== MotionNotify
&&
568 next_event
.xmotion
.window
== xev
->xmotion
.window
&&
569 next_event
.xmotion
.subwindow
== xev
->xmotion
.subwindow
&&
570 next_event
.xmotion
.state
== xev
->xmotion
.state
) {
571 XNextEvent(xev
->xany
.display
, &last_event
);
578 ui::MouseEvent
mouseev(xev
);
579 TranslateAndDispatchMouseEvent(&mouseev
);
586 RootWindow
* RootWindowHostX11::GetRootWindow() {
587 return delegate_
->AsRootWindow();
590 gfx::AcceleratedWidget
RootWindowHostX11::GetAcceleratedWidget() {
594 void RootWindowHostX11::Show() {
595 if (!window_mapped_
) {
596 // Before we map the window, set size hints. Otherwise, some window managers
597 // will ignore toplevel XMoveWindow commands.
598 XSizeHints size_hints
;
599 size_hints
.flags
= PPosition
| PWinGravity
;
600 size_hints
.x
= bounds_
.x();
601 size_hints
.y
= bounds_
.y();
602 // Set StaticGravity so that the window position is not affected by the
603 // frame width when running with window manager.
604 size_hints
.win_gravity
= StaticGravity
;
605 XSetWMNormalHints(xdisplay_
, xwindow_
, &size_hints
);
607 XMapWindow(xdisplay_
, xwindow_
);
609 // We now block until our window is mapped. Some X11 APIs will crash and
610 // burn if passed |xwindow_| before the window is mapped, and XMapWindow is
612 base::MessagePumpX11::Current()->BlockUntilWindowMapped(xwindow_
);
613 window_mapped_
= true;
617 void RootWindowHostX11::Hide() {
618 if (window_mapped_
) {
619 XWithdrawWindow(xdisplay_
, xwindow_
, 0);
620 window_mapped_
= false;
624 void RootWindowHostX11::ToggleFullScreen() {
628 gfx::Rect
RootWindowHostX11::GetBounds() const {
632 void RootWindowHostX11::SetBounds(const gfx::Rect
& bounds
) {
633 // Even if the host window's size doesn't change, aura's root window
634 // size, which is in DIP, changes when the scale changes.
635 float current_scale
= delegate_
->GetDeviceScaleFactor();
636 float new_scale
= gfx::Screen::GetScreenFor(
637 delegate_
->AsRootWindow()->window())->GetDisplayNearestWindow(
638 delegate_
->AsRootWindow()->window()).device_scale_factor();
639 bool origin_changed
= bounds_
.origin() != bounds
.origin();
640 bool size_changed
= bounds_
.size() != bounds
.size();
641 XWindowChanges changes
= {0};
642 unsigned value_mask
= 0;
645 changes
.width
= bounds
.width();
646 changes
.height
= bounds
.height();
647 value_mask
= CWHeight
| CWWidth
;
650 if (origin_changed
) {
651 changes
.x
= bounds
.x();
652 changes
.y
= bounds
.y();
653 value_mask
|= CWX
| CWY
;
656 XConfigureWindow(xdisplay_
, xwindow_
, value_mask
, &changes
);
658 // Assume that the resize will go through as requested, which should be the
659 // case if we're running without a window manager. If there's a window
660 // manager, it can modify or ignore the request, but (per ICCCM) we'll get a
661 // (possibly synthetic) ConfigureNotify about the actual size and correct
664 UpdateIsInternalDisplay();
666 delegate_
->OnHostMoved(bounds
.origin());
667 if (size_changed
|| current_scale
!= new_scale
) {
668 delegate_
->OnHostResized(bounds
.size());
670 delegate_
->AsRootWindow()->window()->SchedulePaintInRect(
671 delegate_
->AsRootWindow()->window()->bounds());
675 gfx::Insets
RootWindowHostX11::GetInsets() const {
679 void RootWindowHostX11::SetInsets(const gfx::Insets
& insets
) {
681 if (pointer_barriers_
) {
683 ConfineCursorToRootWindow();
687 gfx::Point
RootWindowHostX11::GetLocationOnNativeScreen() const {
688 return bounds_
.origin();
691 void RootWindowHostX11::SetCapture() {
692 // TODO(oshima): Grab x input.
695 void RootWindowHostX11::ReleaseCapture() {
696 // TODO(oshima): Release x input.
699 void RootWindowHostX11::SetCursor(gfx::NativeCursor cursor
) {
700 if (cursor
== current_cursor_
)
702 current_cursor_
= cursor
;
703 SetCursorInternal(cursor
);
706 bool RootWindowHostX11::QueryMouseLocation(gfx::Point
* location_return
) {
707 client::CursorClient
* cursor_client
=
708 client::GetCursorClient(GetRootWindow()->window());
709 if (cursor_client
&& !cursor_client
->IsMouseEventsEnabled()) {
710 *location_return
= gfx::Point(0, 0);
714 ::Window root_return
, child_return
;
715 int root_x_return
, root_y_return
, win_x_return
, win_y_return
;
716 unsigned int mask_return
;
717 XQueryPointer(xdisplay_
,
721 &root_x_return
, &root_y_return
,
722 &win_x_return
, &win_y_return
,
724 *location_return
= gfx::Point(max(0, min(bounds_
.width(), win_x_return
)),
725 max(0, min(bounds_
.height(), win_y_return
)));
726 return (win_x_return
>= 0 && win_x_return
< bounds_
.width() &&
727 win_y_return
>= 0 && win_y_return
< bounds_
.height());
730 bool RootWindowHostX11::ConfineCursorToRootWindow() {
731 #if XFIXES_MAJOR >= 5
732 DCHECK(!pointer_barriers_
.get());
733 if (pointer_barriers_
)
735 pointer_barriers_
.reset(new XID
[4]);
736 gfx::Rect
bounds(bounds_
);
737 bounds
.Inset(insets_
);
738 // Horizontal, top barriers.
739 pointer_barriers_
[0] = XFixesCreatePointerBarrier(
740 xdisplay_
, x_root_window_
,
741 bounds
.x(), bounds
.y(), bounds
.right(), bounds
.y(),
744 // Horizontal, bottom barriers.
745 pointer_barriers_
[1] = XFixesCreatePointerBarrier(
746 xdisplay_
, x_root_window_
,
747 bounds
.x(), bounds
.bottom(), bounds
.right(), bounds
.bottom(),
750 // Vertical, left barriers.
751 pointer_barriers_
[2] = XFixesCreatePointerBarrier(
752 xdisplay_
, x_root_window_
,
753 bounds
.x(), bounds
.y(), bounds
.x(), bounds
.bottom(),
756 // Vertical, right barriers.
757 pointer_barriers_
[3] = XFixesCreatePointerBarrier(
758 xdisplay_
, x_root_window_
,
759 bounds
.right(), bounds
.y(), bounds
.right(), bounds
.bottom(),
766 void RootWindowHostX11::UnConfineCursor() {
767 #if XFIXES_MAJOR >= 5
768 if (pointer_barriers_
) {
769 XFixesDestroyPointerBarrier(xdisplay_
, pointer_barriers_
[0]);
770 XFixesDestroyPointerBarrier(xdisplay_
, pointer_barriers_
[1]);
771 XFixesDestroyPointerBarrier(xdisplay_
, pointer_barriers_
[2]);
772 XFixesDestroyPointerBarrier(xdisplay_
, pointer_barriers_
[3]);
773 pointer_barriers_
.reset();
778 void RootWindowHostX11::OnCursorVisibilityChanged(bool show
) {
779 SetCrOSTapPaused(!show
);
782 void RootWindowHostX11::MoveCursorTo(const gfx::Point
& location
) {
783 XWarpPointer(xdisplay_
, None
, x_root_window_
, 0, 0, 0, 0,
784 bounds_
.x() + location
.x(),
785 bounds_
.y() + location
.y());
788 void RootWindowHostX11::PostNativeEvent(
789 const base::NativeEvent
& native_event
) {
792 XEvent xevent
= *native_event
;
793 xevent
.xany
.display
= xdisplay_
;
794 xevent
.xany
.window
= xwindow_
;
796 switch (xevent
.type
) {
803 case ButtonRelease
: {
804 // The fields used below are in the same place for all of events
805 // above. Using xmotion from XEvent's unions to avoid repeating
807 xevent
.xmotion
.root
= x_root_window_
;
808 xevent
.xmotion
.time
= CurrentTime
;
810 gfx::Point
point(xevent
.xmotion
.x
, xevent
.xmotion
.y
);
811 delegate_
->AsRootWindow()->host()->ConvertPointToNativeScreen(&point
);
812 xevent
.xmotion
.x_root
= point
.x();
813 xevent
.xmotion
.y_root
= point
.y();
818 XSendEvent(xdisplay_
, xwindow_
, False
, 0, &xevent
);
821 void RootWindowHostX11::OnDeviceScaleFactorChanged(
822 float device_scale_factor
) {
825 void RootWindowHostX11::PrepareForShutdown() {
826 base::MessagePumpX11::Current()->RemoveDispatcherForWindow(xwindow_
);
829 void RootWindowHostX11::OnWindowInitialized(Window
* window
) {
832 void RootWindowHostX11::OnRootWindowInitialized(RootWindow
* root_window
) {
833 // UpdateIsInternalDisplay relies on:
834 // 1. delegate_ pointing to RootWindow - available after SetDelegate.
835 // 2. RootWindow's kDisplayIdKey property set - available by the time
836 // RootWindow::Init is called.
837 // (set in DisplayManager::CreateRootWindowForDisplay)
838 // Ready when NotifyRootWindowInitialized is called from RootWindow::Init.
839 if (!delegate_
|| root_window
!= GetRootWindow())
841 UpdateIsInternalDisplay();
843 // We have to enable Tap-to-click by default because the cursor is set to
844 // visible in Shell::InitRootWindowController.
845 SetCrOSTapPaused(false);
848 bool RootWindowHostX11::DispatchEventForRootWindow(
849 const base::NativeEvent
& event
) {
850 switch (event
->type
) {
851 case ConfigureNotify
:
852 DCHECK_EQ(x_root_window_
, event
->xconfigure
.event
);
853 x_root_bounds_
.SetRect(event
->xconfigure
.x
, event
->xconfigure
.y
,
854 event
->xconfigure
.width
, event
->xconfigure
.height
);
858 DispatchXI2Event(event
);
865 void RootWindowHostX11::DispatchXI2Event(const base::NativeEvent
& event
) {
866 ui::TouchFactory
* factory
= ui::TouchFactory::GetInstance();
868 if (!factory
->ShouldProcessXI2Event(xev
))
871 TRACE_EVENT1("input", "RootWindowHostX11::DispatchXI2Event",
873 (ui::EventTimeForNow() - ui::EventTimeFromNative(event
)).
876 ui::EventType type
= ui::EventTypeFromNative(xev
);
878 int num_coalesced
= 0;
881 case ui::ET_TOUCH_MOVED
:
882 case ui::ET_TOUCH_PRESSED
:
883 case ui::ET_TOUCH_CANCELLED
:
884 case ui::ET_TOUCH_RELEASED
: {
885 #if defined(OS_CHROMEOS)
886 // Bail out early before generating a ui::TouchEvent if this event
887 // is not within the range of this RootWindow. Converting an xevent
888 // to ui::TouchEvent might change the state of the global touch tracking
889 // state, e.g. touch release event can remove the touch id from the
890 // record, and doing this multiple time when there are multiple
891 // RootWindow will cause problem. So only generate the ui::TouchEvent
892 // when we are sure it belongs to this RootWindow.
893 if (base::SysInfo::IsRunningOnChromeOS() &&
894 !bounds_
.Contains(ui::EventLocationFromNative(xev
)))
896 #endif // defined(OS_CHROMEOS)
897 ui::TouchEvent
touchev(xev
);
898 #if defined(USE_XI2_MT)
899 // Ignore touch events with touch press happening on the side bezel.
900 if (!IsSideBezelsEnabled()) {
901 uint32 tracking_id
= (1 << touchev
.touch_id());
902 if (type
== ui::ET_TOUCH_PRESSED
&&
903 touch_calibrate_
->IsEventOnSideBezels(xev
, bounds_
))
904 bezel_tracking_ids_
|= tracking_id
;
905 if (bezel_tracking_ids_
& tracking_id
) {
906 if (type
== ui::ET_TOUCH_CANCELLED
|| type
== ui::ET_TOUCH_RELEASED
)
907 bezel_tracking_ids_
=
908 (bezel_tracking_ids_
| tracking_id
) ^ tracking_id
;
912 #endif // defined(USE_XI2_MT)
913 #if defined(OS_CHROMEOS)
914 if (base::SysInfo::IsRunningOnChromeOS()) {
915 // X maps the touch-surface to the size of the X root-window.
916 // In multi-monitor setup, Coordinate Transformation Matrix
917 // repositions the touch-surface onto part of X root-window
918 // containing aura root-window corresponding to the touchscreen.
919 // However, if aura root-window has non-zero origin,
920 // we need to relocate the event into aura root-window coordinates.
921 touchev
.Relocate(bounds_
.origin());
922 #if defined(USE_XI2_MT)
923 if (is_internal_display_
)
924 touch_calibrate_
->Calibrate(&touchev
, bounds_
);
925 #endif // defined(USE_XI2_MT)
927 #endif // defined(OS_CHROMEOS)
928 delegate_
->OnHostTouchEvent(&touchev
);
931 case ui::ET_MOUSE_MOVED
:
932 case ui::ET_MOUSE_DRAGGED
:
933 case ui::ET_MOUSE_PRESSED
:
934 case ui::ET_MOUSE_RELEASED
:
935 case ui::ET_MOUSE_ENTERED
:
936 case ui::ET_MOUSE_EXITED
: {
937 if (type
== ui::ET_MOUSE_MOVED
|| type
== ui::ET_MOUSE_DRAGGED
) {
938 // If this is a motion event, we want to coalesce all pending motion
939 // events that are at the top of the queue.
940 num_coalesced
= ui::CoalescePendingMotionEvents(xev
, &last_event
);
941 if (num_coalesced
> 0)
944 if (mouse_move_filter_
&& mouse_move_filter_
->Filter(xev
))
946 } else if (type
== ui::ET_MOUSE_PRESSED
||
947 type
== ui::ET_MOUSE_RELEASED
) {
948 XIDeviceEvent
* xievent
=
949 static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
);
950 int button
= xievent
->detail
;
951 if (button
== kBackMouseButton
|| button
== kForwardMouseButton
) {
952 if (type
== ui::ET_MOUSE_RELEASED
)
954 client::UserActionClient
* gesture_client
=
955 client::GetUserActionClient(delegate_
->AsRootWindow()->window());
956 if (gesture_client
) {
957 bool reverse_direction
=
958 ui::IsTouchpadEvent(xev
) && ui::IsNaturalScrollEnabled();
959 gesture_client
->OnUserAction(
960 (button
== kBackMouseButton
&& !reverse_direction
) ||
961 (button
== kForwardMouseButton
&& reverse_direction
) ?
962 client::UserActionClient::BACK
:
963 client::UserActionClient::FORWARD
);
968 ui::MouseEvent
mouseev(xev
);
969 TranslateAndDispatchMouseEvent(&mouseev
);
972 case ui::ET_MOUSEWHEEL
: {
973 ui::MouseWheelEvent
mouseev(xev
);
974 TranslateAndDispatchMouseEvent(&mouseev
);
977 case ui::ET_SCROLL_FLING_START
:
978 case ui::ET_SCROLL_FLING_CANCEL
:
979 case ui::ET_SCROLL
: {
980 ui::ScrollEvent
scrollev(xev
);
981 delegate_
->OnHostScrollEvent(&scrollev
);
984 case ui::ET_UMA_DATA
:
992 // If we coalesced an event we need to free its cookie.
993 if (num_coalesced
> 0)
994 XFreeEventData(xev
->xgeneric
.display
, &last_event
.xcookie
);
997 bool RootWindowHostX11::IsWindowManagerPresent() {
998 // Per ICCCM 2.8, "Manager Selections", window managers should take ownership
999 // of WM_Sn selections (where n is a screen number).
1000 return XGetSelectionOwner(
1001 xdisplay_
, atom_cache_
.GetAtom("WM_S0")) != None
;
1004 void RootWindowHostX11::SetCursorInternal(gfx::NativeCursor cursor
) {
1005 XDefineCursor(xdisplay_
, xwindow_
, cursor
.platform());
1008 void RootWindowHostX11::TranslateAndDispatchMouseEvent(
1009 ui::MouseEvent
* event
) {
1010 Window
* root_window
= GetRootWindow()->window();
1011 client::ScreenPositionClient
* screen_position_client
=
1012 client::GetScreenPositionClient(root_window
);
1013 gfx::Rect
local(bounds_
.size());
1015 if (screen_position_client
&& !local
.Contains(event
->location())) {
1016 gfx::Point
location(event
->location());
1017 // In order to get the correct point in screen coordinates
1018 // during passive grab, we first need to find on which host window
1019 // the mouse is on, and find out the screen coordinates on that
1020 // host window, then convert it back to this host window's coordinate.
1021 screen_position_client
->ConvertHostPointToScreen(root_window
, &location
);
1022 screen_position_client
->ConvertPointFromScreen(root_window
, &location
);
1023 root_window
->GetDispatcher()->ConvertPointToHost(&location
);
1024 event
->set_location(location
);
1025 event
->set_root_location(location
);
1027 delegate_
->OnHostMouseEvent(event
);
1030 void RootWindowHostX11::UpdateIsInternalDisplay() {
1031 Window
* root_window
= GetRootWindow()->window();
1032 gfx::Screen
* screen
= gfx::Screen::GetScreenFor(root_window
);
1033 gfx::Display display
= screen
->GetDisplayNearestWindow(root_window
);
1034 is_internal_display_
= display
.IsInternal();
1037 void RootWindowHostX11::SetCrOSTapPaused(bool state
) {
1038 #if defined(OS_CHROMEOS)
1039 if (!ui::IsXInput2Available())
1041 // Temporarily pause tap-to-click when the cursor is hidden.
1042 Atom prop
= atom_cache_
.GetAtom("Tap Paused");
1043 unsigned char value
= state
;
1044 XIDeviceList dev_list
=
1045 ui::DeviceListCacheX::GetInstance()->GetXI2DeviceList(xdisplay_
);
1047 // Only slave pointer devices could possibly have tap-paused property.
1048 for (int i
= 0; i
< dev_list
.count
; i
++) {
1049 if (dev_list
[i
].use
== XISlavePointer
) {
1052 unsigned long old_nvalues
, bytes
;
1053 unsigned char* data
;
1054 int result
= XIGetProperty(xdisplay_
, dev_list
[i
].deviceid
, prop
, 0, 0,
1055 False
, AnyPropertyType
, &old_type
, &old_format
,
1056 &old_nvalues
, &bytes
, &data
);
1057 if (result
!= Success
)
1060 XIChangeProperty(xdisplay_
, dev_list
[i
].deviceid
, prop
, XA_INTEGER
, 8,
1061 PropModeReplace
, &value
, 1);
1068 RootWindowHost
* RootWindowHost::Create(const gfx::Rect
& bounds
) {
1069 return new RootWindowHostX11(bounds
);
1073 gfx::Size
RootWindowHost::GetNativeScreenSize() {
1074 ::XDisplay
* xdisplay
= gfx::GetXDisplay();
1075 return gfx::Size(DisplayWidth(xdisplay
, 0), DisplayHeight(xdisplay
, 0));
1080 void SetUseOverrideRedirectWindowByDefault(bool override_redirect
) {
1081 default_override_redirect
= override_redirect
;