e5dd2c198cae8ab4cbdc695d15c41d98bbc74b38
[platform/framework/web/crosswalk.git] / src / ui / views / widget / desktop_aura / x11_desktop_handler.cc
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.
4
5 #include "ui/views/widget/desktop_aura/x11_desktop_handler.h"
6
7 #include <X11/Xatom.h>
8 #include <X11/Xlib.h>
9
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"
14
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"
18 #endif
19
20 namespace {
21
22 const char* kAtomsToCache[] = {
23   "_NET_ACTIVE_WINDOW",
24   "_NET_WM_STATE_HIDDEN",
25   NULL
26 };
27
28 // Our global instance. Deleted when our Env() is deleted.
29 views::X11DesktopHandler* g_handler = NULL;
30
31 }  // namespace
32
33 namespace views {
34
35 // static
36 X11DesktopHandler* X11DesktopHandler::get() {
37   if (!g_handler)
38     g_handler = new X11DesktopHandler;
39
40   return g_handler;
41 }
42
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);
51
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);
57
58   ::Window active_window;
59   wm_supports_active_window_ =
60     ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &active_window) &&
61     active_window;
62 }
63
64 X11DesktopHandler::~X11DesktopHandler() {
65   aura::Env::GetInstance()->RemoveObserver(this);
66   base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
67 }
68
69 void X11DesktopHandler::ActivateWindow(::Window window) {
70   if (wm_supports_active_window_) {
71     DCHECK_EQ(gfx::GetXDisplay(), xdisplay_);
72
73     XEvent xclient;
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;
84
85     XSendEvent(xdisplay_, x_root_window_, False,
86                SubstructureRedirectMask | SubstructureNotifyMask,
87                &xclient);
88   } else {
89     XRaiseWindow(xdisplay_, window);
90
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);
95
96     OnActiveWindowChanged(window);
97   }
98 }
99
100 void X11DesktopHandler::DeactivateWindow(::Window window) {
101   DCHECK(IsActiveWindow(window));
102
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.
107     NOTIMPLEMENTED();
108     return;
109   }
110
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.
116   if (!to_activate) {
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.";
120     return;
121   }
122
123   ActivateWindow(to_activate);
124 }
125
126 bool X11DesktopHandler::IsActiveWindow(::Window window) const {
127   return window == current_window_;
128 }
129
130 void X11DesktopHandler::ProcessXEvent(const base::NativeEvent& event) {
131   switch (event->type) {
132     case EnterNotify:
133       if (event->xcrossing.focus == True &&
134           current_window_ != event->xcrossing.window)
135         OnActiveWindowChanged(event->xcrossing.window);
136       break;
137     case LeaveNotify:
138       if (event->xcrossing.focus == False &&
139           current_window_ == event->xcrossing.window)
140         OnActiveWindowChanged(None);
141       break;
142     default:
143       NOTREACHED();
144   }
145 }
146
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");
152
153       if (event->xproperty.window == x_root_window_ &&
154           event->xproperty.atom == active_window_atom) {
155         ::Window window;
156         if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) &&
157             window) {
158           OnActiveWindowChanged(window);
159         }
160       }
161       break;
162     }
163   }
164
165   return true;
166 }
167
168 void X11DesktopHandler::OnWindowInitialized(aura::Window* window) {
169 }
170
171 void X11DesktopHandler::OnWillDestroyEnv() {
172   g_handler = NULL;
173   delete this;
174 }
175
176 void X11DesktopHandler::OnActiveWindowChanged(::Window xid) {
177   if (current_window_ == xid)
178     return;
179   DesktopWindowTreeHostX11* old_host =
180       views::DesktopWindowTreeHostX11::GetHostForXID(current_window_);
181   if (old_host)
182     old_host->HandleNativeWidgetActivationChanged(false);
183
184   DesktopWindowTreeHostX11* new_host =
185       views::DesktopWindowTreeHostX11::GetHostForXID(xid);
186   if (new_host)
187     new_host->HandleNativeWidgetActivationChanged(true);
188
189   current_window_ = xid;
190 }
191
192 ::Window X11DesktopHandler::GetNextToActivateInStack(
193     const std::vector< ::Window >& windows) {
194   DCHECK(current_window_);
195
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_);
199
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());
203
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))
208       continue;
209
210     std::vector<Atom>::const_iterator hidden_atom =
211         std::find(wm_states.begin(),
212                   wm_states.end(),
213                   atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN"));
214
215     if (hidden_atom != wm_states.end())
216       continue;
217
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.
224
225     return *it;
226   }
227
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.
231   return 0;
232 }
233
234 }  // namespace views