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/views/widget/desktop_aura/x11_desktop_handler.h"
10 #include "base/message_loop/message_loop.h"
11 #include "ui/aura/env.h"
12 #include "ui/aura/root_window.h"
13 #include "ui/base/x/x11_util.h"
15 #if !defined(OS_CHROMEOS)
16 #include "ui/views/ime/input_method.h"
17 #include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h"
22 const char* kAtomsToCache[] = {
24 "_NET_WM_STATE_HIDDEN",
28 // Our global instance. Deleted when our Env() is deleted.
29 views::X11DesktopHandler* g_handler = NULL;
36 X11DesktopHandler* X11DesktopHandler::get() {
38 g_handler = new X11DesktopHandler;
43 X11DesktopHandler::X11DesktopHandler()
44 : xdisplay_(gfx::GetXDisplay()),
45 x_root_window_(DefaultRootWindow(xdisplay_)),
46 current_window_(None),
47 atom_cache_(xdisplay_, kAtomsToCache),
48 wm_supports_active_window_(false) {
49 base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this);
50 aura::Env::GetInstance()->AddObserver(this);
52 XWindowAttributes attr;
53 XGetWindowAttributes(xdisplay_, x_root_window_, &attr);
54 XSelectInput(xdisplay_, x_root_window_,
55 attr.your_event_mask | PropertyChangeMask |
56 StructureNotifyMask | SubstructureNotifyMask);
58 ::Window active_window;
59 wm_supports_active_window_ =
60 ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &active_window) &&
64 X11DesktopHandler::~X11DesktopHandler() {
65 aura::Env::GetInstance()->RemoveObserver(this);
66 base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
69 void X11DesktopHandler::ActivateWindow(::Window window) {
70 if (wm_supports_active_window_) {
71 DCHECK_EQ(gfx::GetXDisplay(), xdisplay_);
74 memset(&xclient, 0, sizeof(xclient));
75 xclient.type = ClientMessage;
76 xclient.xclient.window = window;
77 xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
78 xclient.xclient.format = 32;
79 xclient.xclient.data.l[0] = 1; // Specified we are an app.
80 xclient.xclient.data.l[1] = CurrentTime;
81 xclient.xclient.data.l[2] = None;
82 xclient.xclient.data.l[3] = 0;
83 xclient.xclient.data.l[4] = 0;
85 XSendEvent(xdisplay_, x_root_window_, False,
86 SubstructureRedirectMask | SubstructureNotifyMask,
89 XRaiseWindow(xdisplay_, window);
91 // XRaiseWindow will not give input focus to the window. We now need to ask
92 // the X server to do that. Note that the call will raise an X error if the
93 // window is not mapped.
94 XSetInputFocus(xdisplay_, window, RevertToParent, CurrentTime);
96 OnActiveWindowChanged(window);
100 void X11DesktopHandler::DeactivateWindow(::Window window) {
101 DCHECK(IsActiveWindow(window));
103 std::vector< ::Window > windows;
104 if (!ui::GetXWindowStack(x_root_window_, &windows)) {
105 // TODO(mlamouri): use XQueryTree in case of _NET_CLIENT_LIST_STACKING is
106 // not supported by the WM.
111 // To deactivate the window, we want to activate the window below in z-order.
112 ::Window to_activate = GetNextToActivateInStack(windows);
113 // TODO(mlamouri): some WM might not have a "Desktop" to activate when there
114 // are no other windows. In which case, we should handle the case where
115 // |to_activate| == 0.
117 DLOG(WARNING) << "Not deactivating because there are no other window to "
118 << "activate. If you see this please file a bug and mention "
119 << "the window manager you are using.";
123 ActivateWindow(to_activate);
126 bool X11DesktopHandler::IsActiveWindow(::Window window) const {
127 return window == current_window_;
130 void X11DesktopHandler::ProcessXEvent(const base::NativeEvent& event) {
131 switch (event->type) {
133 if (event->xcrossing.focus == True &&
134 current_window_ != event->xcrossing.window)
135 OnActiveWindowChanged(event->xcrossing.window);
138 if (event->xcrossing.focus == False &&
139 current_window_ == event->xcrossing.window)
140 OnActiveWindowChanged(None);
147 bool X11DesktopHandler::Dispatch(const base::NativeEvent& event) {
148 // Check for a change to the active window.
149 switch (event->type) {
150 case PropertyNotify: {
151 ::Atom active_window_atom = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
153 if (event->xproperty.window == x_root_window_ &&
154 event->xproperty.atom == active_window_atom) {
156 if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) &&
158 OnActiveWindowChanged(window);
168 void X11DesktopHandler::OnWindowInitialized(aura::Window* window) {
171 void X11DesktopHandler::OnWillDestroyEnv() {
176 void X11DesktopHandler::OnActiveWindowChanged(::Window xid) {
177 if (current_window_ == xid)
179 DesktopWindowTreeHostX11* old_host =
180 views::DesktopWindowTreeHostX11::GetHostForXID(current_window_);
182 old_host->HandleNativeWidgetActivationChanged(false);
184 DesktopWindowTreeHostX11* new_host =
185 views::DesktopWindowTreeHostX11::GetHostForXID(xid);
187 new_host->HandleNativeWidgetActivationChanged(true);
189 current_window_ = xid;
192 ::Window X11DesktopHandler::GetNextToActivateInStack(
193 const std::vector< ::Window >& windows) {
194 DCHECK(current_window_);
196 // We start by doing a fast forward in the stack to find the active window.
197 std::vector< ::Window >::const_iterator it =
198 std::find(windows.begin(), windows.end(), current_window_);
200 // We expect to find the currently active window in the |windows| list. Not
201 // finding it is an unexpected behavior.
202 CHECK(it != windows.end());
204 // After that, we want to activate the next window that is not minimized.
205 for (++it; it != windows.end(); ++it) {
206 std::vector<Atom> wm_states;
207 if (!ui::GetAtomArrayProperty(*it, "_NET_WM_STATE", &wm_states))
210 std::vector<Atom>::const_iterator hidden_atom =
211 std::find(wm_states.begin(),
213 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN"));
215 if (hidden_atom != wm_states.end())
218 // We could do more checks like verifying that _NET_WM_WINDOW_TYPE is a
219 // value we are happy with or that _NET_WM_STATE does not contain
220 // _NET_WM_STATE_SKIP_PAGER or _NET_WM_STATE_SKIP_TASKBAR but those calls
221 // would cost and so far, the tested WM put the special window before the
222 // active one in the stack and the desktop at the end of the stack. That
223 // matches pretty well the behavior we are looking for.
228 // If we reached that point, that means we have not found an appropriate
229 // window to activate. There is nothing we can do about it and the caller
230 // should take care of doing the right thing.