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 aeadc78f2fe40a9863c39daa451852a86f78cf9f..11f8fe57ec53ad1cc3ff8ece36767da798cdb445 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", |
@@ -182,6 +183,11 @@ DesktopWindowTreeHostX11::DesktopWindowTreeHostX11( |
custom_window_shape_(false), |
urgency_hint_set_(false), |
activatable_(true), |
+ has_pointer_(false), |
+ has_pointer_grab_(false), |
+ has_pointer_focus_(false), |
+ has_window_focus_(false), |
+ has_focus_(false), |
close_widget_factory_(this) { |
} |
@@ -230,20 +236,75 @@ gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowOuterBounds() const { |
return window_shape_.get(); |
} |
-void DesktopWindowTreeHostX11::HandleNativeWidgetActivationChanged( |
- bool active) { |
- if (active) { |
+void DesktopWindowTreeHostX11::BeforeActivationStateChanged() { |
+ was_active_ = IsActive(); |
+ had_pointer_grab_ = has_pointer_grab_; |
+ had_window_focus_ = has_window_focus_; |
+} |
+ |
+void DesktopWindowTreeHostX11::AfterActivationStateChanged() { |
+ if (had_pointer_grab_ && !has_pointer_grab_) |
+ dispatcher()->OnHostLostMouseGrab(); |
+ if (had_window_focus_ && !has_window_focus_) |
+ OnHostLostWindowCapture(); |
danakj
2016/08/19 21:43:45
Why do we not do this if we had pointer focus but
Tom (Use chromium acct)
2016/08/22 20:27:07
You're right, I was unclear about the definition o
danakj
2016/08/26 19:58:43
Agree, thanks for figuring out what it means
|
+ |
+ 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(); |
} |
+ if (was_active_ != IsActive()) { |
+ desktop_native_widget_aura_->HandleActivationChanged(IsActive()); |
+ native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); |
+ } |
+} |
- desktop_native_widget_aura_->HandleActivationChanged(active); |
+void DesktopWindowTreeHostX11::OnCrossingEvent(bool enter, |
+ bool focus, |
danakj
2016/08/19 21:43:45
focus_in_window_or_ancestor?
Tom (Use chromium acct)
2016/08/22 20:27:07
Done.
|
+ int detail) { |
+ if (detail == NotifyInferior) |
danakj
2016/08/19 21:43:45
When the mouse moves from the root into |xwindow_|
danakj
2016/08/19 23:17:32
Oh I see.
EnterNotify with Inferior means we're c
Tom (Use chromium acct)
2016/08/22 20:27:07
Done.
|
+ return; |
+ |
+ BeforeActivationStateChanged(); |
+ |
+ has_pointer_ = enter; |
+ if (focus && !has_window_focus_) |
danakj
2016/08/19 21:43:45
// If focus is not in an ancestor then this crossi
Tom (Use chromium acct)
2016/08/22 20:27:07
Done. I wrote something different though.
|
+ has_pointer_focus_ = has_pointer_; |
- native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); |
+ AfterActivationStateChanged(); |
+} |
+ |
+void DesktopWindowTreeHostX11::OnFocusEvent(bool focus_in, |
+ int mode, |
+ int detail) { |
+ if (detail == NotifyInferior) |
danakj
2016/08/19 23:29:58
Can you say the same thing here moreorless?
Tom (Use chromium acct)
2016/08/22 20:27:07
Done.
|
+ return; |
+ BeforeActivationStateChanged(); |
+ if (mode != NotifyWhileGrabbed && detail != NotifyPointer) |
danakj
2016/08/19 23:29:58
Can you throw some comments here kinda like the he
danakj
2016/08/19 23:59:30
Also can you explain by ignore Pointer here in a c
danakj
2016/08/19 23:59:30
Maybe better would be to say that while grabbed we
Tom (Use chromium acct)
2016/08/22 20:27:07
Done.
Tom (Use chromium acct)
2016/08/22 20:27:07
Done.
|
+ has_focus_ = focus_in; |
+ if (mode != NotifyGrab && mode != NotifyUngrab && |
+ mode != XINotifyPassiveGrab && mode != XINotifyPassiveUngrab) { |
+ if (detail != NotifyPointer) |
+ has_window_focus_ = focus_in; |
+ if (has_pointer_) { |
+ switch (detail) { |
+ case NotifyAncestor: |
+ case NotifyVirtual: |
danakj
2016/08/19 23:59:30
Can you leave a comment why NonLinear(Virtual) are
Tom (Use chromium acct)
2016/08/22 20:27:07
Done.
|
+ has_pointer_focus_ = !focus_in; |
+ break; |
+ case NotifyPointer: |
+ has_pointer_focus_ = focus_in; |
+ break; |
+ default: |
+ break; |
+ } |
+ } |
+ } |
+ ignore_input_ = false; |
+ AfterActivationStateChanged(); |
} |
void DesktopWindowTreeHostX11::AddObserver( |
@@ -308,8 +369,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 +467,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 +487,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 +503,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 +674,77 @@ void DesktopWindowTreeHostX11::SetShape( |
} |
void DesktopWindowTreeHostX11::Activate() { |
- if (!window_mapped_) |
+ if (!IsVisible() || !activatable_) |
return; |
- X11DesktopHandler::get()->ActivateWindow(xwindow_); |
+ BeforeActivationStateChanged(); |
+ |
+ ignore_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_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_focus_ || has_pointer_focus_) && !ignore_input_; |
danakj
2016/08/19 23:29:58
FWIW normally an X window will show itself as visi
Tom (Use chromium acct)
2016/08/22 20:27:07
The cross-platform code seems to want a window to
danakj
2016/08/22 20:32:42
OK just to be clear then, when alt-tabbing does th
|
+ |
+ // is_active => window_mapped_ |
+ // !window_mapped_ => !is_active |
+ DCHECK(!is_active || window_mapped_); |
+ |
+ return is_active; |
} |
void DesktopWindowTreeHostX11::Maximize() { |
@@ -650,7 +765,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 +1086,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 +1111,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); |
@@ -1424,7 +1538,7 @@ void DesktopWindowTreeHostX11::OnFrameExtentsUpdated() { |
} |
void DesktopWindowTreeHostX11::UpdateMinAndMaxSize() { |
- if (!window_mapped_) |
+ if (!IsVisible()) |
return; |
gfx::Size minimum_in_pixels = |
@@ -1480,7 +1594,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 +1794,14 @@ 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_input_ = show_state == ui::SHOW_STATE_INACTIVE; |
+ unsigned long wm_user_time_ms = |
+ ignore_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 +1863,15 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( |
switch (xev->type) { |
case EnterNotify: |
case LeaveNotify: { |
+ OnCrossingEvent(xev->type == EnterNotify, xev->xcrossing.focus, |
+ 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 +1887,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,18 +1918,12 @@ 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: { |
danakj
2016/08/19 21:43:45
nit: {} are not needed since there's no vars decla
Tom (Use chromium acct)
2016/08/22 20:27:07
Done.
|
+ OnFocusEvent(xev->type == FocusIn, event->xfocus.mode, |
+ event->xfocus.detail); |
break; |
+ } |
case ConfigureNotify: { |
DCHECK_EQ(xwindow_, xev->xconfigure.window); |
DCHECK_EQ(xwindow_, xev->xconfigure.event); |
@@ -1860,6 +1963,22 @@ 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, |
+ enter_event->detail); |
+ break; |
+ case XI_FocusIn: |
+ case XI_FocusOut: |
+ OnFocusEvent(enter_event->evtype == XI_FocusIn, enter_event->mode, |
danakj
2016/08/19 23:59:30
Would you consider rewriting the mode to non-XI co
Tom (Use chromium acct)
2016/08/22 20:27:07
Done.
|
+ enter_event->detail); |
+ break; |
+ default: |
+ break; |
+ } |
+ |
ui::EventType type = ui::EventTypeFromNative(xev); |
XEvent last_event; |
int num_coalesced = 0; |
@@ -1941,7 +2060,13 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( |
break; |
} |
case UnmapNotify: { |
danakj
2016/08/19 21:43:45
jw do we care about DestroyNotify? Presumably no..
Tom (Use chromium acct)
2016/08/22 20:27:07
no
|
+ window_mapped_ = false; |
wait_for_unmap_ = false; |
+ has_pointer_ = false; |
+ has_pointer_grab_ = false; |
+ has_pointer_focus_ = false; |
+ has_window_focus_ = false; |
+ has_focus_ = false; |
FOR_EACH_OBSERVER(DesktopWindowTreeHostObserverX11, |
observer_list_, |
OnWindowUnmapped(xwindow_)); |