198b9c660707d67c422c0e6587b8bda82d42bd71
[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_window_tree_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     case FocusIn:
143       if (event->xfocus.mode == NotifyNormal &&
144           current_window_ != event->xfocus.window)
145         OnActiveWindowChanged(event->xfocus.window);
146       break;
147     default:
148       NOTREACHED();
149   }
150 }
151
152 uint32_t X11DesktopHandler::Dispatch(const base::NativeEvent& event) {
153   // Check for a change to the active window.
154   switch (event->type) {
155     case PropertyNotify: {
156       ::Atom active_window_atom = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
157
158       if (event->xproperty.window == x_root_window_ &&
159           event->xproperty.atom == active_window_atom) {
160         ::Window window;
161         if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) &&
162             window) {
163           OnActiveWindowChanged(window);
164         }
165       }
166       break;
167     }
168   }
169
170   return POST_DISPATCH_NONE;
171 }
172
173 void X11DesktopHandler::OnWindowInitialized(aura::Window* window) {
174 }
175
176 void X11DesktopHandler::OnWillDestroyEnv() {
177   g_handler = NULL;
178   delete this;
179 }
180
181 void X11DesktopHandler::OnActiveWindowChanged(::Window xid) {
182   if (current_window_ == xid)
183     return;
184   DesktopWindowTreeHostX11* old_host =
185       views::DesktopWindowTreeHostX11::GetHostForXID(current_window_);
186   if (old_host)
187     old_host->HandleNativeWidgetActivationChanged(false);
188
189   DesktopWindowTreeHostX11* new_host =
190       views::DesktopWindowTreeHostX11::GetHostForXID(xid);
191   if (new_host)
192     new_host->HandleNativeWidgetActivationChanged(true);
193
194   current_window_ = xid;
195 }
196
197 ::Window X11DesktopHandler::GetNextToActivateInStack(
198     const std::vector< ::Window >& windows) {
199   DCHECK(current_window_);
200
201   // We start by doing a fast forward in the stack to find the active window.
202   std::vector< ::Window >::const_iterator it =
203       std::find(windows.begin(), windows.end(), current_window_);
204
205   // We expect to find the currently active window in the |windows| list. Not
206   // finding it is an unexpected behavior.
207   CHECK(it != windows.end());
208
209   // After that, we want to activate the next window that is not minimized.
210   for (++it; it != windows.end(); ++it) {
211     std::vector<Atom> wm_states;
212     if (!ui::GetAtomArrayProperty(*it, "_NET_WM_STATE", &wm_states))
213       continue;
214
215     std::vector<Atom>::const_iterator hidden_atom =
216         std::find(wm_states.begin(),
217                   wm_states.end(),
218                   atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN"));
219
220     if (hidden_atom != wm_states.end())
221       continue;
222
223     // We could do more checks like verifying that _NET_WM_WINDOW_TYPE is a
224     // value we are happy with or that _NET_WM_STATE does not contain
225     // _NET_WM_STATE_SKIP_PAGER or _NET_WM_STATE_SKIP_TASKBAR but those calls
226     // would cost and so far, the tested WM put the special window before the
227     // active one in the stack and the desktop at the end of the stack. That
228     // matches pretty well the behavior we are looking for.
229
230     return *it;
231   }
232
233   // If we reached that point, that means we have not found an appropriate
234   // window to activate. There is nothing we can do about it and the caller
235   // should take care of doing the right thing.
236   return 0;
237 }
238
239 }  // namespace views