Upstream version 9.38.198.0
[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/window_event_dispatcher.h"
13 #include "ui/base/x/x11_foreign_window_manager.h"
14 #include "ui/base/x/x11_menu_list.h"
15 #include "ui/base/x/x11_util.h"
16 #include "ui/events/platform/platform_event_source.h"
17 #include "ui/gfx/x/x11_error_tracker.h"
18 #include "ui/views/ime/input_method.h"
19 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
20
21 namespace {
22
23 const char* kAtomsToCache[] = {
24   "_NET_ACTIVE_WINDOW",
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       wm_user_time_ms_(0),
47       current_window_(None),
48       current_window_active_state_(NOT_ACTIVE),
49       atom_cache_(xdisplay_, kAtomsToCache),
50       wm_supports_active_window_(false) {
51   if (ui::PlatformEventSource::GetInstance())
52     ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
53   aura::Env::GetInstance()->AddObserver(this);
54
55   XWindowAttributes attr;
56   XGetWindowAttributes(xdisplay_, x_root_window_, &attr);
57   XSelectInput(xdisplay_, x_root_window_,
58                attr.your_event_mask | PropertyChangeMask |
59                StructureNotifyMask | SubstructureNotifyMask);
60
61   ::Window active_window;
62   wm_supports_active_window_ =
63     ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &active_window) &&
64     active_window;
65 }
66
67 X11DesktopHandler::~X11DesktopHandler() {
68   aura::Env::GetInstance()->RemoveObserver(this);
69   if (ui::PlatformEventSource::GetInstance())
70     ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
71 }
72
73 void X11DesktopHandler::ActivateWindow(::Window window) {
74   if (current_window_ == window &&
75       current_window_active_state_ == NOT_ACTIVE) {
76     // |window| is most likely still active wrt to the X server. Undo the
77     // changes made in DeactivateWindow().
78     OnActiveWindowChanged(window, ACTIVE);
79
80     // Go through the regular activation path such that calling
81     // DeactivateWindow() and ActivateWindow() immediately afterwards results
82     // in an active X window.
83   }
84
85   XRaiseWindow(xdisplay_, window);
86
87   if (wm_supports_active_window_) {
88     DCHECK_EQ(gfx::GetXDisplay(), xdisplay_);
89
90     XEvent xclient;
91     memset(&xclient, 0, sizeof(xclient));
92     xclient.type = ClientMessage;
93     xclient.xclient.window = window;
94     xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
95     xclient.xclient.format = 32;
96     xclient.xclient.data.l[0] = 1;  // Specified we are an app.
97     xclient.xclient.data.l[1] = wm_user_time_ms_;
98     xclient.xclient.data.l[2] = None;
99     xclient.xclient.data.l[3] = 0;
100     xclient.xclient.data.l[4] = 0;
101
102     XSendEvent(xdisplay_, x_root_window_, False,
103                SubstructureRedirectMask | SubstructureNotifyMask,
104                &xclient);
105   } else {
106     // XRaiseWindow will not give input focus to the window. We now need to ask
107     // the X server to do that. Note that the call will raise an X error if the
108     // window is not mapped.
109     XSetInputFocus(xdisplay_, window, RevertToParent, CurrentTime);
110
111     OnActiveWindowChanged(window, ACTIVE);
112   }
113 }
114
115 void X11DesktopHandler::DeactivateWindow(::Window window) {
116   if (!IsActiveWindow(window))
117     return;
118
119   XLowerWindow(xdisplay_, window);
120
121   // Per ICCCM: http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
122   // "Clients should not give up the input focus of their own volition.
123   // They should ignore input that they receive instead."
124   //
125   // There is nothing else that we can do. Pretend that we have been
126   // deactivated and ignore keyboard input in DesktopWindowTreeHostX11.
127   OnActiveWindowChanged(window, NOT_ACTIVE);
128 }
129
130 bool X11DesktopHandler::IsActiveWindow(::Window window) const {
131   return window == current_window_ && current_window_active_state_ == ACTIVE;
132 }
133
134 void X11DesktopHandler::ProcessXEvent(XEvent* event) {
135   switch (event->type) {
136     case FocusIn:
137       if (current_window_ != event->xfocus.window)
138         OnActiveWindowChanged(event->xfocus.window, ACTIVE);
139       break;
140     case FocusOut:
141       if (current_window_ == event->xfocus.window)
142         OnActiveWindowChanged(None, NOT_ACTIVE);
143       break;
144     default:
145       NOTREACHED();
146   }
147 }
148
149 bool X11DesktopHandler::CanDispatchEvent(const ui::PlatformEvent& event) {
150   return event->type == CreateNotify || event->type == DestroyNotify ||
151          (event->type == PropertyNotify &&
152           event->xproperty.window == x_root_window_);
153 }
154
155 uint32_t X11DesktopHandler::DispatchEvent(const ui::PlatformEvent& event) {
156   switch (event->type) {
157     case PropertyNotify: {
158       // Check for a change to the active window.
159       CHECK_EQ(x_root_window_, event->xproperty.window);
160       ::Atom active_window_atom = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
161       if (event->xproperty.atom == active_window_atom) {
162         ::Window window;
163         if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) &&
164             window) {
165           OnActiveWindowChanged(window, ACTIVE);
166         }
167       }
168       break;
169     }
170
171     case CreateNotify:
172       OnWindowCreatedOrDestroyed(event->type, event->xcreatewindow.window);
173       break;
174     case DestroyNotify:
175       OnWindowCreatedOrDestroyed(event->type, event->xdestroywindow.window);
176       break;
177     default:
178       NOTREACHED();
179   }
180
181   return ui::POST_DISPATCH_NONE;
182 }
183
184 void X11DesktopHandler::OnWindowInitialized(aura::Window* window) {
185 }
186
187 void X11DesktopHandler::OnWillDestroyEnv() {
188   g_handler = NULL;
189   delete this;
190 }
191
192 void X11DesktopHandler::OnActiveWindowChanged(::Window xid,
193                                               ActiveState active_state) {
194   if (current_window_ == xid && current_window_active_state_ == active_state)
195     return;
196
197   if (current_window_active_state_ == ACTIVE) {
198     DesktopWindowTreeHostX11* old_host =
199         views::DesktopWindowTreeHostX11::GetHostForXID(current_window_);
200     if (old_host)
201       old_host->HandleNativeWidgetActivationChanged(false);
202   }
203
204   // Update the current window ID to effectively change the active widget.
205   current_window_ = xid;
206   current_window_active_state_ = active_state;
207
208   if (active_state == ACTIVE) {
209     DesktopWindowTreeHostX11* new_host =
210         views::DesktopWindowTreeHostX11::GetHostForXID(xid);
211     if (new_host)
212       new_host->HandleNativeWidgetActivationChanged(true);
213   }
214 }
215
216 void X11DesktopHandler::OnWindowCreatedOrDestroyed(int event_type,
217                                                    XID window) {
218   // Menus created by Chrome can be drag and drop targets. Since they are
219   // direct children of the screen root window and have override_redirect
220   // we cannot use regular _NET_CLIENT_LIST_STACKING property to find them
221   // and use a separate cache to keep track of them.
222   // TODO(varkha): Implement caching of all top level X windows and their
223   // coordinates and stacking order to eliminate repeated calls to the X server
224   // during mouse movement, drag and shaping events.
225   if (event_type == CreateNotify) {
226     // The window might be destroyed if the message pump did not get a chance to
227     // run but we can safely ignore the X error.
228     gfx::X11ErrorTracker error_tracker;
229     ui::XMenuList::GetInstance()->MaybeRegisterMenu(window);
230   } else {
231     ui::XMenuList::GetInstance()->MaybeUnregisterMenu(window);
232   }
233
234   if (event_type == DestroyNotify) {
235     // Notify the XForeignWindowManager that |window| has been destroyed.
236     ui::XForeignWindowManager::GetInstance()->OnWindowDestroyed(window);
237   }
238 }
239
240 }  // namespace views