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..ed01f0eb5b7e9ca140e32bbde46bce053f107848 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", |
@@ -230,20 +231,30 @@ 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(); |
+ |
+ 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); |
- |
- native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); |
+ if (was_active_ != IsActive()) { |
+ desktop_native_widget_aura_->HandleActivationChanged(IsActive()); |
+ native_widget_delegate_->AsWidget()->GetRootView()->SchedulePaint(); |
danakj
2016/08/11 21:48:20
This really only needs to be done when becoming ac
Tom (Use chromium acct)
2016/08/15 18:42:46
Not only when it becomes active, when the activati
|
+ } |
} |
void DesktopWindowTreeHostX11::AddObserver( |
@@ -308,8 +319,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. |
@@ -426,8 +437,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(); |
} |
@@ -614,22 +624,69 @@ void DesktopWindowTreeHostX11::SetShape( |
} |
void DesktopWindowTreeHostX11::Activate() { |
- if (!window_mapped_) |
+ if (!window_mapped_ || !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")); |
+ |
+ 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] = GetTimestamp(); |
+ xclient.xclient.data.l[2] = None; |
danakj
2016/08/11 21:48:20
This is actually a lie if another chrome window is
Tom (Use chromium acct)
2016/08/15 18:42:46
Done.
|
+ 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, CurrentTime); |
danakj
2016/08/11 21:48:20
Can you explain/document why CurrentTime and not G
Tom (Use chromium acct)
2016/08/15 18:42:46
Copy/paste bug. Fixed.
|
+ // 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. |
+ return window_mapped_ && |
danakj
2016/08/11 21:48:20
the has focus things can't be true while window_ma
Tom (Use chromium acct)
2016/08/15 18:42:46
Done.
|
+ (has_window_focus_ || has_pointer_grab_ || has_pointer_focus_) && |
+ !ignore_input_; |
} |
void DesktopWindowTreeHostX11::Maximize() { |
@@ -1480,10 +1537,18 @@ 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); |
} |
} |
+Time DesktopWindowTreeHostX11::GetTimestamp() { |
+ ui::X11EventSource* event_source = ui::X11EventSource::GetInstance(); |
+ Time time = event_source->last_seen_server_time(); |
+ if (time == CurrentTime) { |
danakj
2016/08/11 21:48:20
This feels like an approximation for what we want.
Tom (Use chromium acct)
2016/08/15 18:42:45
We don't currently do that :(
But I think this is
|
+ time = event_source->UpdateLastSeenServerTime(); |
+ } |
+ return time; |
+} |
+ |
void DesktopWindowTreeHostX11::SetWMSpecState(bool enabled, |
::Atom state1, |
::Atom state2) { |
@@ -1681,17 +1746,17 @@ 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(); |
+ has_pointer_ = false; |
danakj
2016/08/11 21:48:20
I'd expect these to all be initialized to false, a
Tom (Use chromium acct)
2016/08/15 18:42:45
shouldn't happen, done
|
+ has_pointer_grab_ = false; |
+ has_pointer_focus_ = false; |
+ has_window_focus_ = false; |
+ ignore_input_ = show_state == ui::SHOW_STATE_INACTIVE; |
+ unsigned long wm_user_time_ms = ignore_input_ ? 0 : GetTimestamp(); |
danakj
2016/08/11 21:48:20
So here we want to use the "window launch time" in
Tom (Use chromium acct)
2016/08/15 18:42:46
We need the window launch time for _NET_ACTIVE_WIN
|
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(); |
@@ -1755,11 +1820,23 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent( |
case LeaveNotify: { |
// 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; |
+ BeforeActivationStateChanged(); |
+ |
+ if (xev->xcrossing.mode == NotifyGrab) |
+ has_pointer_grab_ = xev->type == EnterNotify; |
+ else if (xev->xcrossing.mode == NotifyUngrab) |
+ has_pointer_grab_ = false; |
+ |
+ has_pointer_ = xev->type == EnterNotify; |
+ if(!xev->xcrossing.focus && has_window_focus_) { |
danakj
2016/08/11 21:48:20
git cl format
Tom (Use chromium acct)
2016/08/15 18:42:45
Done.
|
+ has_pointer_focus_ = has_pointer_; |
danakj
2016/08/11 21:48:20
In the header we claimed that has_window_focus_ an
Tom (Use chromium acct)
2016/08/15 18:42:46
Thanks for catching this terrible logic :)
The ! s
|
+ ignore_input_ = false; |
danakj
2016/08/11 21:48:20
I'm hesitant about the whole ignore_input_ thing.
Tom (Use chromium acct)
2016/08/15 18:42:46
I don't know when we call Deactivate() in producti
|
+ } |
+ AfterActivationStateChanged(); |
+ |
ui::MouseEvent mouse_event(xev); |
DispatchMouseEvent(&mouse_event); |
break; |
@@ -1777,8 +1854,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 +1885,34 @@ 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: { |
+ BeforeActivationStateChanged(); |
+ bool focus_in = xev->type == FocusIn; |
+ auto mode = event->xfocus.mode; |
+ bool grab_notify = mode == NotifyGrab || mode == XINotifyPassiveGrab || |
+ mode == NotifyUngrab || mode == XINotifyPassiveUngrab; |
+ switch (xev->xfocus.detail) { |
+ case NotifyAncestor: |
+ case NotifyVirtual: |
+ if (has_pointer_ && !grab_notify) |
+ has_pointer_focus_ = !focus_in; |
danakj
2016/08/11 21:48:20
I think you only want to do this for FocusOut. Oth
Tom (Use chromium acct)
2016/08/15 18:42:46
Doesn't it say to do this for FocusIn as well?
|
+ // fallthrough |
+ case NotifyNonlinear: |
+ case NotifyNonlinearVirtual: |
+ if (!grab_notify) |
+ has_window_focus_ = focus_in; |
danakj
2016/08/11 21:48:20
The focus_tracking.txt doc says has_window_focus s
Tom (Use chromium acct)
2016/08/15 18:42:46
Probably a mistake in the doc. NotifyPointer shou
|
+ break; |
+ case NotifyPointer: |
+ if (!grab_notify) |
+ has_pointer_focus_ = focus_in; |
+ default: |
+ break; |
+ } |
+ ignore_input_ = false; |
+ AfterActivationStateChanged(); |
break; |
+ } |
case ConfigureNotify: { |
DCHECK_EQ(xwindow_, xev->xconfigure.window); |
DCHECK_EQ(xwindow_, xev->xconfigure.event); |