Index: ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc |
diff --git a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc |
index 5327938528bad44ced671b3c73c0927bc596f3af..62068f5774b191095037c48749da60adc215812c 100644 |
--- a/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc |
+++ b/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc |
@@ -91,6 +91,7 @@ const char* kAtomsToCache[] = { |
"UTF8_STRING", |
"WM_DELETE_WINDOW", |
"WM_PROTOCOLS", |
+ "_NET_ACTIVE_WINDOW", |
"_NET_FRAME_EXTENTS", |
"_NET_WM_CM_S0", |
"_NET_WM_DESKTOP", |
@@ -155,6 +156,24 @@ std::vector<::Window> GetParentsList(XDisplay* xdisplay, ::Window window) { |
return result; |
} |
+int XI2ModeToXMode(int xi2_mode) { |
+ switch (xi2_mode) { |
+ case XINotifyNormal: |
+ return NotifyNormal; |
+ case XINotifyGrab: |
+ case XINotifyPassiveGrab: |
+ return NotifyGrab; |
+ case XINotifyUngrab: |
+ case XINotifyPassiveUngrab: |
+ return NotifyUngrab; |
+ case XINotifyWhileGrabbed: |
+ return NotifyWhileGrabbed; |
+ default: |
+ NOTREACHED(); |
+ return NotifyNormal; |
+ } |
+} |
+ |
} // namespace |
//////////////////////////////////////////////////////////////////////////////// |
@@ -181,9 +200,12 @@ DesktopWindowTreeHostX11::DesktopWindowTreeHostX11( |
window_parent_(NULL), |
custom_window_shape_(false), |
urgency_hint_set_(false), |
+ has_pointer_grab_(false), |
activatable_(true), |
- close_widget_factory_(this) { |
-} |
+ has_pointer_(false), |
+ has_window_focus_(false), |
+ has_pointer_focus_(false), |
+ close_widget_factory_(this) {} |
DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() { |
window()->ClearProperty(kHostForRootWindow); |
@@ -230,20 +252,147 @@ gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowOuterBounds() const { |
return window_shape_.get(); |
} |
-void DesktopWindowTreeHostX11::HandleNativeWidgetActivationChanged( |
- bool active) { |
- if (active) { |
+void DesktopWindowTreeHostX11::BeforeActivationStateChanged() { |
+ was_active_ = IsActive(); |
+ had_pointer_ = has_pointer_; |
+ had_pointer_grab_ = has_pointer_grab_; |
+ had_window_focus_ = has_window_focus_; |
+} |
+ |
+void DesktopWindowTreeHostX11::AfterActivationStateChanged() { |
+ if (had_pointer_grab_ && !has_pointer_grab_) |
+ dispatcher()->OnHostLostMouseGrab(); |
+ |
+ bool had_pointer_capture = had_pointer_ || had_pointer_grab_; |
+ bool has_pointer_capture = has_pointer_ || has_pointer_grab_; |
+ if (had_pointer_capture && !has_pointer_capture) |
+ OnHostLostWindowCapture(); |
+ |
+ if (!was_active_ && IsActive()) { |
FlashFrame(false); |
OnHostActivated(); |
+ // TODO(thomasanderson): Remove this window shuffling and use XWindowCache |
+ // instead. |
open_windows().remove(xwindow_); |
open_windows().insert(open_windows().begin(), xwindow_); |
- } else { |
- ReleaseCapture(); |
} |
- desktop_native_widget_aura_->HandleActivationChanged(active); |
+ if (was_active_ != IsActive()) { |
+ desktop_native_widget_aura_->HandleActivationChanged(IsActive()); |
+ native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); |
+ } |
+} |
+ |
+void DesktopWindowTreeHostX11::OnCrossingEvent(bool enter, |
+ bool focus_in_window_or_ancestor, |
+ int mode, |
+ int detail) { |
+ // NotifyInferior on a crossing event means the pointer moved into or out of a |
+ // child window, but the pointer is still within |xwindow_|. |
+ if (detail == NotifyInferior) |
+ return; |
- native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); |
+ BeforeActivationStateChanged(); |
+ |
+ if (mode == NotifyGrab) |
+ has_pointer_grab_ = enter; |
+ else if (mode == NotifyUngrab) |
+ has_pointer_grab_ = false; |
+ |
+ has_pointer_ = enter; |
+ if (focus_in_window_or_ancestor && !has_window_focus_) { |
+ // If we reach this point, we know the focus is in an ancestor or the |
+ // pointer root. The definition of |has_pointer_focus_| is (An ancestor |
+ // window or the PointerRoot is focused) && |has_pointer_|. Therefore, we |
+ // can just use |has_pointer_| in the assignment. The transitions for when |
+ // the focus changes are handled in OnFocusEvent(). |
+ has_pointer_focus_ = has_pointer_; |
+ } |
+ |
+ AfterActivationStateChanged(); |
+} |
+ |
+void DesktopWindowTreeHostX11::OnFocusEvent(bool focus_in, |
+ int mode, |
+ int detail) { |
+ // NotifyInferior on a focus event means the focus moved into or out of a |
+ // child window, but the focus is still within |xwindow_|. |
+ if (detail == NotifyInferior) |
+ return; |
+ |
+ bool notify_grab = mode == NotifyGrab || mode == NotifyUngrab; |
+ |
+ BeforeActivationStateChanged(); |
+ |
+ // For every focus change, the X server sends normal focus events which are |
+ // useful for tracking |has_window_focus_|, but supplements these events with |
+ // NotifyPointer events which are only useful for tracking pointer focus. |
+ |
+ // For |has_pointer_focus_| and |has_window_focus_|, we continue tracking |
+ // state during a grab, but ignore grab/ungrab events themselves. |
+ if (!notify_grab && detail != NotifyPointer) |
+ has_window_focus_ = focus_in; |
+ |
+ if (!notify_grab && has_pointer_) { |
+ switch (detail) { |
+ case NotifyAncestor: |
+ case NotifyVirtual: |
+ // If we reach this point, we know |has_pointer_| was true before and |
+ // after this event. Since the definition of |has_pointer_focus_| is |
+ // (An ancestor window or the PointerRoot is focused) && |has_pointer_|, |
+ // we only need to worry about transitions on the first conjunct. |
+ // Therefore, |has_pointer_focus_| will become true when: |
+ // 1. Focus moves from |xwindow_| to an ancestor |
+ // (FocusOut with NotifyAncestor) |
+ // 2. Focus moves from a decendant of |xwindow_| to an ancestor |
+ // (FocusOut with NotifyVirtual) |
+ // |has_pointer_focus_| will become false when: |
+ // 1. Focus moves from an ancestor to |xwindow_| |
+ // (FocusIn with NotifyAncestor) |
+ // 2. Focus moves from an ancestor to a child of |xwindow_| |
+ // (FocusIn with NotifyVirtual) |
+ has_pointer_focus_ = !focus_in; |
+ break; |
+ case NotifyPointer: |
+ // The remaining cases for |has_pointer_focus_| becoming true are: |
+ // 3. Focus moves from |xwindow_| to the PointerRoot |
+ // 4. Focus moves from a decendant of |xwindow_| to the PointerRoot |
+ // 5. Focus moves from None to the PointerRoot |
+ // 6. Focus moves from Other to the PointerRoot |
+ // 7. Focus moves from None to an ancestor of |xwindow_| |
+ // 8. Focus moves from Other to an ancestor fo |xwindow_| |
+ // In each case, we will get a FocusIn with a detail of NotifyPointer. |
+ // The remaining cases for |has_pointer_focus_| becoming false are: |
+ // 3. Focus moves from the PointerRoot to |xwindow_| |
+ // 4. Focus moves from the PointerRoot to a decendant of |xwindow| |
+ // 5. Focus moves from the PointerRoot to None |
+ // 6. Focus moves from an ancestor of |xwindow_| to None |
+ // 7. Focus moves from the PointerRoot to Other |
+ // 8. Focus moves from an ancestor of |xwindow_| to Other |
+ // In each case, we will get a FocusOut with a detail of NotifyPointer. |
+ has_pointer_focus_ = focus_in; |
+ break; |
+ case NotifyNonlinear: |
+ case NotifyNonlinearVirtual: |
+ // We get Nonlinear(Virtual) events when |
+ // 1. Focus moves from Other to |xwindow_| |
+ // (FocusIn with NotifyNonlinear) |
+ // 2. Focus moves from Other to a decendant of |xwindow_| |
+ // (FocusIn with NotifyNonlinearVirtual) |
+ // 3. Focus moves from |xwindow_| to Other |
+ // (FocusOut with NotifyNonlinear) |
+ // 4. Focus moves from a decendant of |xwindow_| to Other |
+ // (FocusOut with NotifyNonlinearVirtual) |
+ // |has_pointer_focus_| should be false before and after this event. |
+ has_pointer_focus_ = false; |
+ default: |
+ break; |
+ } |
+ } |
+ |
+ ignore_keyboard_input_ = false; |
+ |
+ AfterActivationStateChanged(); |
} |
void DesktopWindowTreeHostX11::AddObserver( |
@@ -308,8 +457,8 @@ void DesktopWindowTreeHostX11::OnNativeWidgetCreated( |
window()->SetProperty(kViewsWindowForRootWindow, content_window_); |
window()->SetProperty(kHostForRootWindow, this); |
- // Ensure that the X11DesktopHandler exists so that it dispatches activation |
- // messages to us. |
+ // Ensure that the X11DesktopHandler exists so that it tracks create/destroy |
+ // notify events. |
X11DesktopHandler::get(); |
// TODO(erg): Unify this code once the other consumer goes away. |
@@ -406,7 +555,7 @@ void DesktopWindowTreeHostX11::ShowWindowWithState( |
ui::WindowShowState show_state) { |
if (compositor()) |
compositor()->SetVisible(true); |
- if (!window_mapped_) |
+ if (!IsVisible()) |
MapWindow(show_state); |
switch (show_state) { |
@@ -426,8 +575,7 @@ void DesktopWindowTreeHostX11::ShowWindowWithState( |
// Makes the window activated by default if the state is not INACTIVE or |
// MINIMIZED. |
if (show_state != ui::SHOW_STATE_INACTIVE && |
- show_state != ui::SHOW_STATE_MINIMIZED && |
- activatable_) { |
+ show_state != ui::SHOW_STATE_MINIMIZED) { |
Activate(); |
} |
@@ -443,7 +591,7 @@ void DesktopWindowTreeHostX11::ShowMaximizedWithBounds( |
} |
bool DesktopWindowTreeHostX11::IsVisible() const { |
- return window_mapped_; |
+ return window_mapped_ && !wait_for_unmap_; |
} |
void DesktopWindowTreeHostX11::SetSize(const gfx::Size& requested_size) { |
@@ -614,22 +762,81 @@ void DesktopWindowTreeHostX11::SetShape( |
} |
void DesktopWindowTreeHostX11::Activate() { |
- if (!window_mapped_) |
+ if (!IsVisible() || !activatable_) |
return; |
- X11DesktopHandler::get()->ActivateWindow(xwindow_); |
+ BeforeActivationStateChanged(); |
+ |
+ ignore_keyboard_input_ = false; |
+ |
+ // wmii says that it supports _NET_ACTIVE_WINDOW but does not. |
+ // https://code.google.com/p/wmii/issues/detail?id=266 |
+ static bool wm_supports_active_window = |
+ ui::GuessWindowManager() != ui::WM_WMII && |
+ ui::WmSupportsHint(atom_cache_.GetAtom("_NET_ACTIVE_WINDOW")); |
+ |
+ Time timestamp = ui::X11EventSource::GetInstance()->GetTimestamp(); |
+ |
+ if (wm_supports_active_window) { |
+ XEvent xclient; |
+ memset(&xclient, 0, sizeof(xclient)); |
+ xclient.type = ClientMessage; |
+ xclient.xclient.window = xwindow_; |
+ xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"); |
+ xclient.xclient.format = 32; |
+ xclient.xclient.data.l[0] = 1; // Specified we are an app. |
+ xclient.xclient.data.l[1] = timestamp; |
+ // TODO(thomasanderson): if another chrome window is active, specify that in |
+ // data.l[2]. The EWMH spec claims this may make the WM more likely to |
+ // service our _NET_ACTIVE_WINDOW request. |
+ xclient.xclient.data.l[2] = None; |
+ xclient.xclient.data.l[3] = 0; |
+ xclient.xclient.data.l[4] = 0; |
+ |
+ XSendEvent(xdisplay_, x_root_window_, False, |
+ SubstructureRedirectMask | SubstructureNotifyMask, &xclient); |
+ } else { |
+ XRaiseWindow(xdisplay_, xwindow_); |
+ // Directly ask the X server to give focus to the window. Note that the call |
+ // will raise an X error if the window is not mapped. |
+ XSetInputFocus(xdisplay_, xwindow_, RevertToParent, timestamp); |
+ // At this point, we know we will receive focus, and some tests depend on a |
+ // window being IsActive() immediately after an Activate(), so just set this |
+ // state now. |
+ has_pointer_focus_ = false; |
+ has_window_focus_ = true; |
+ } |
+ AfterActivationStateChanged(); |
} |
void DesktopWindowTreeHostX11::Deactivate() { |
- if (!IsActive()) |
- return; |
+ BeforeActivationStateChanged(); |
+ |
+ // Ignore future input events. |
+ ignore_keyboard_input_ = true; |
ReleaseCapture(); |
- X11DesktopHandler::get()->DeactivateWindow(xwindow_); |
+ XLowerWindow(xdisplay_, xwindow_); |
+ |
+ AfterActivationStateChanged(); |
} |
bool DesktopWindowTreeHostX11::IsActive() const { |
- return X11DesktopHandler::get()->IsActiveWindow(xwindow_); |
+ // Focus and stacking order are independent in X11. Since we cannot guarantee |
+ // a window is topmost iff it has focus, just use the focus state to determine |
+ // if a window is active. Note that Activate() and Deactivate() change the |
+ // stacking order in addition to changing the focus state. |
+ bool is_active = |
+ (has_window_focus_ || has_pointer_focus_) && !ignore_keyboard_input_; |
+ |
+ // is_active => window_mapped_ |
+ // !window_mapped_ => !is_active |
+ DCHECK(!is_active || window_mapped_); |
+ |
+ // |has_window_focus_| and |has_pointer_focus_| are mutually exclusive. |
+ DCHECK(!has_window_focus_ || !has_pointer_focus_); |
+ |
+ return is_active; |
} |
void DesktopWindowTreeHostX11::Maximize() { |
@@ -650,7 +857,7 @@ void DesktopWindowTreeHostX11::Maximize() { |
// Some WMs do not respect maximization hints on unmapped windows, so we |
// save this one for later too. |
- should_maximize_after_map_ = !window_mapped_; |
+ should_maximize_after_map_ = !IsVisible(); |
// When we are in the process of requesting to maximize a window, we can |
// accurately keep track of our restored bounds instead of relying on the |
@@ -971,7 +1178,7 @@ void DesktopWindowTreeHostX11::SizeConstraintsChanged() { |
gfx::Transform DesktopWindowTreeHostX11::GetRootTransform() const { |
display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay(); |
- if (window_mapped_) { |
+ if (IsVisible()) { |
aura::Window* win = const_cast<aura::Window*>(window()); |
display = display::Screen::GetScreen()->GetDisplayNearestWindow(win); |
} |
@@ -996,9 +1203,8 @@ void DesktopWindowTreeHostX11::ShowImpl() { |
} |
void DesktopWindowTreeHostX11::HideImpl() { |
- if (window_mapped_) { |
+ if (IsVisible()) { |
XWithdrawWindow(xdisplay_, xwindow_, 0); |
- window_mapped_ = false; |
wait_for_unmap_ = true; |
} |
native_widget_delegate_->OnNativeWidgetVisibilityChanged(false); |
@@ -1086,7 +1292,9 @@ void DesktopWindowTreeHostX11::SetCapture() { |
if (old_capturer) |
old_capturer->OnHostLostWindowCapture(); |
- GrabPointer(xwindow_, true, None); |
+ // If the pointer is already in |xwindow_|, we will not get a crossing event |
+ // with a mode of NotifyGrab, so we must record the grab state manually. |
+ has_pointer_grab_ |= !GrabPointer(xwindow_, true, None); |
} |
void DesktopWindowTreeHostX11::ReleaseCapture() { |
@@ -1096,6 +1304,7 @@ void DesktopWindowTreeHostX11::ReleaseCapture() { |
// asynchronous is likely inconsequential. |
g_current_capture = NULL; |
UngrabPointer(); |
+ has_pointer_grab_ = false; |
OnHostLostWindowCapture(); |
} |
@@ -1424,7 +1633,7 @@ void DesktopWindowTreeHostX11::OnFrameExtentsUpdated() { |
} |
void DesktopWindowTreeHostX11::UpdateMinAndMaxSize() { |
- if (!window_mapped_) |
+ if (!IsVisible()) |
return; |
gfx::Size minimum_in_pixels = |
@@ -1480,7 +1689,6 @@ void DesktopWindowTreeHostX11::UpdateWMUserTime( |
PropModeReplace, |
reinterpret_cast<const unsigned char *>(&wm_user_time_ms), |
1); |
- X11DesktopHandler::get()->set_wm_user_time_ms(wm_user_time_ms); |
} |
} |
@@ -1681,17 +1889,16 @@ void DesktopWindowTreeHostX11::MapWindow(ui::WindowShowState show_state) { |
// If SHOW_STATE_INACTIVE, tell the window manager not to focus the window |
// when mapping. This is done by setting the _NET_WM_USER_TIME to 0. See e.g. |
// http://standards.freedesktop.org/wm-spec/latest/ar01s05.html |
- unsigned long wm_user_time_ms = (show_state == ui::SHOW_STATE_INACTIVE) ? |
- 0 : X11DesktopHandler::get()->wm_user_time_ms(); |
+ ignore_keyboard_input_ = show_state == ui::SHOW_STATE_INACTIVE; |
+ unsigned long wm_user_time_ms = |
+ ignore_keyboard_input_ |
+ ? 0 |
+ : ui::X11EventSource::GetInstance()->GetTimestamp(); |
if (show_state == ui::SHOW_STATE_INACTIVE || wm_user_time_ms != 0) { |
- XChangeProperty(xdisplay_, |
- xwindow_, |
- atom_cache_.GetAtom("_NET_WM_USER_TIME"), |
- XA_CARDINAL, |
- 32, |
- PropModeReplace, |
- reinterpret_cast<const unsigned char *>(&wm_user_time_ms), |
- 1); |
+ XChangeProperty( |
+ xdisplay_, xwindow_, atom_cache_.GetAtom("_NET_WM_USER_TIME"), |
+ XA_CARDINAL, 32, PropModeReplace, |
+ reinterpret_cast<const unsigned char*>(&wm_user_time_ms), 1); |
} |
ui::X11EventSource* event_source = ui::X11EventSource::GetInstance(); |
@@ -1753,15 +1960,15 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( |
switch (xev->type) { |
case EnterNotify: |
case LeaveNotify: { |
+ OnCrossingEvent(xev->type == EnterNotify, xev->xcrossing.focus, |
+ xev->xcrossing.mode, xev->xcrossing.detail); |
+ |
// Ignore EventNotify and LeaveNotify events from children of |xwindow_|. |
// NativeViewGLSurfaceGLX adds a child to |xwindow_|. |
- // TODO(pkotwicz|tdanderson): Figure out whether the suppression is |
- // necessary. crbug.com/385716 |
- if (xev->xcrossing.detail == NotifyInferior) |
- break; |
- |
- ui::MouseEvent mouse_event(xev); |
- DispatchMouseEvent(&mouse_event); |
+ if (xev->xcrossing.detail != NotifyInferior) { |
+ ui::MouseEvent mouse_event(xev); |
+ DispatchMouseEvent(&mouse_event); |
+ } |
break; |
} |
case Expose: { |
@@ -1777,8 +1984,7 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( |
} |
case KeyRelease: { |
// There is no way to deactivate a window in X11 so ignore input if |
- // window is supposed to be 'inactive'. See comments in |
- // X11DesktopHandler::DeactivateWindow() for more details. |
+ // window is supposed to be 'inactive'. |
if (!IsActive() && !HasCapture()) |
break; |
@@ -1809,17 +2015,10 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( |
} |
break; |
} |
- case FocusOut: |
- if (xev->xfocus.mode != NotifyGrab) { |
- ReleaseCapture(); |
- OnHostLostWindowCapture(); |
- X11DesktopHandler::get()->ProcessXEvent(xev); |
- } else { |
- dispatcher()->OnHostLostMouseGrab(); |
- } |
- break; |
case FocusIn: |
- X11DesktopHandler::get()->ProcessXEvent(xev); |
+ case FocusOut: |
+ OnFocusEvent(xev->type == FocusIn, event->xfocus.mode, |
+ event->xfocus.detail); |
break; |
case ConfigureNotify: { |
DCHECK_EQ(xwindow_, xev->xconfigure.window); |
@@ -1860,6 +2059,23 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( |
if (!factory->ShouldProcessXI2Event(xev)) |
break; |
+ XIEnterEvent* enter_event = static_cast<XIEnterEvent*>(xev->xcookie.data); |
+ switch (static_cast<XIEvent*>(xev->xcookie.data)->evtype) { |
+ case XI_Enter: |
+ case XI_Leave: |
+ OnCrossingEvent(enter_event->evtype == XI_Enter, enter_event->focus, |
+ XI2ModeToXMode(enter_event->mode), |
+ enter_event->detail); |
+ break; |
+ case XI_FocusIn: |
+ case XI_FocusOut: |
+ OnFocusEvent(enter_event->evtype == XI_FocusIn, |
+ XI2ModeToXMode(enter_event->mode), enter_event->detail); |
+ break; |
+ default: |
+ break; |
+ } |
+ |
ui::EventType type = ui::EventTypeFromNative(xev); |
XEvent last_event; |
int num_coalesced = 0; |
@@ -1941,7 +2157,12 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( |
break; |
} |
case UnmapNotify: { |
+ window_mapped_ = false; |
wait_for_unmap_ = false; |
+ has_pointer_ = false; |
+ has_pointer_grab_ = false; |
+ has_pointer_focus_ = false; |
+ has_window_focus_ = false; |
FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11, |
observer_list_, |
OnWindowUnmapped(xwindow_)); |