Disable tab_switching.tough_energy_cases on Linux
[chromium-blink-merge.git] / ui / views / widget / desktop_aura / x11_desktop_handler.cc
blob33b6347958efd04a654db25e6edc6ef636047394
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"
7 #include <X11/Xatom.h>
8 #include <X11/Xlib.h>
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"
21 namespace {
23 const char* kAtomsToCache[] = {
24 "_NET_ACTIVE_WINDOW",
25 NULL
28 // Our global instance. Deleted when our Env() is deleted.
29 views::X11DesktopHandler* g_handler = NULL;
31 } // namespace
33 namespace views {
35 // static
36 X11DesktopHandler* X11DesktopHandler::get() {
37 if (!g_handler)
38 g_handler = new X11DesktopHandler;
40 return g_handler;
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);
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);
61 if (ui::GuessWindowManager() == ui::WM_WMII) {
62 // wmii says that it supports _NET_ACTIVE_WINDOW but does not.
63 // https://code.google.com/p/wmii/issues/detail?id=266
64 wm_supports_active_window_ = false;
65 } else {
66 wm_supports_active_window_ =
67 ui::WmSupportsHint(atom_cache_.GetAtom("_NET_ACTIVE_WINDOW"));
71 X11DesktopHandler::~X11DesktopHandler() {
72 aura::Env::GetInstance()->RemoveObserver(this);
73 if (ui::PlatformEventSource::GetInstance())
74 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
77 void X11DesktopHandler::ActivateWindow(::Window window) {
78 if ((current_window_ == None || current_window_ == window) &&
79 current_window_active_state_ == NOT_ACTIVE) {
80 // |window| is most likely still active wrt to the X server. Undo the
81 // changes made in DeactivateWindow().
82 OnActiveWindowChanged(window, ACTIVE);
84 // Go through the regular activation path such that calling
85 // DeactivateWindow() and ActivateWindow() immediately afterwards results
86 // in an active X window.
89 XRaiseWindow(xdisplay_, window);
91 if (wm_supports_active_window_) {
92 DCHECK_EQ(gfx::GetXDisplay(), xdisplay_);
94 XEvent xclient;
95 memset(&xclient, 0, sizeof(xclient));
96 xclient.type = ClientMessage;
97 xclient.xclient.window = window;
98 xclient.xclient.message_type = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
99 xclient.xclient.format = 32;
100 xclient.xclient.data.l[0] = 1; // Specified we are an app.
101 xclient.xclient.data.l[1] = wm_user_time_ms_;
102 xclient.xclient.data.l[2] = None;
103 xclient.xclient.data.l[3] = 0;
104 xclient.xclient.data.l[4] = 0;
106 XSendEvent(xdisplay_, x_root_window_, False,
107 SubstructureRedirectMask | SubstructureNotifyMask,
108 &xclient);
109 } else {
110 // XRaiseWindow will not give input focus to the window. We now need to ask
111 // the X server to do that. Note that the call will raise an X error if the
112 // window is not mapped.
113 XSetInputFocus(xdisplay_, window, RevertToParent, CurrentTime);
115 OnActiveWindowChanged(window, ACTIVE);
119 void X11DesktopHandler::DeactivateWindow(::Window window) {
120 if (!IsActiveWindow(window))
121 return;
123 XLowerWindow(xdisplay_, window);
125 // Per ICCCM: http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
126 // "Clients should not give up the input focus of their own volition.
127 // They should ignore input that they receive instead."
129 // There is nothing else that we can do. Pretend that we have been
130 // deactivated and ignore keyboard input in DesktopWindowTreeHostX11.
131 OnActiveWindowChanged(window, NOT_ACTIVE);
134 bool X11DesktopHandler::IsActiveWindow(::Window window) const {
135 return window == current_window_ && current_window_active_state_ == ACTIVE;
138 void X11DesktopHandler::ProcessXEvent(XEvent* event) {
139 // Ignore focus events in modes other than NotifyNormal (i.e. NotifyGrab), as
140 // they are always sent when the pointer is over our window, even if the
141 // input focus is in a different window.
142 if (event->xfocus.mode != NotifyNormal)
143 return;
145 switch (event->type) {
146 case FocusIn:
147 if (current_window_ != event->xfocus.window)
148 OnActiveWindowChanged(event->xfocus.window, ACTIVE);
149 break;
150 case FocusOut:
151 if (current_window_ == event->xfocus.window)
152 OnActiveWindowChanged(None, NOT_ACTIVE);
153 break;
154 default:
155 NOTREACHED();
159 bool X11DesktopHandler::CanDispatchEvent(const ui::PlatformEvent& event) {
160 return event->type == CreateNotify || event->type == DestroyNotify ||
161 (event->type == PropertyNotify &&
162 event->xproperty.window == x_root_window_);
165 uint32_t X11DesktopHandler::DispatchEvent(const ui::PlatformEvent& event) {
166 switch (event->type) {
167 case PropertyNotify: {
168 // Check for a change to the active window.
169 CHECK_EQ(x_root_window_, event->xproperty.window);
170 ::Atom active_window_atom = atom_cache_.GetAtom("_NET_ACTIVE_WINDOW");
171 if (event->xproperty.atom == active_window_atom) {
172 ::Window window;
173 if (ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &window) &&
174 window) {
175 OnActiveWindowChanged(window, ACTIVE);
178 break;
181 case CreateNotify:
182 OnWindowCreatedOrDestroyed(event->type, event->xcreatewindow.window);
183 break;
184 case DestroyNotify:
185 OnWindowCreatedOrDestroyed(event->type, event->xdestroywindow.window);
186 break;
187 default:
188 NOTREACHED();
191 return ui::POST_DISPATCH_NONE;
194 void X11DesktopHandler::OnWindowInitialized(aura::Window* window) {
197 void X11DesktopHandler::OnWillDestroyEnv() {
198 g_handler = NULL;
199 delete this;
202 void X11DesktopHandler::OnActiveWindowChanged(::Window xid,
203 ActiveState active_state) {
204 if (current_window_ == xid && current_window_active_state_ == active_state)
205 return;
207 if (current_window_active_state_ == ACTIVE) {
208 DesktopWindowTreeHostX11* old_host =
209 views::DesktopWindowTreeHostX11::GetHostForXID(current_window_);
210 if (old_host)
211 old_host->HandleNativeWidgetActivationChanged(false);
214 // Update the current window ID to effectively change the active widget.
215 current_window_ = xid;
216 current_window_active_state_ = active_state;
218 if (active_state == ACTIVE) {
219 DesktopWindowTreeHostX11* new_host =
220 views::DesktopWindowTreeHostX11::GetHostForXID(xid);
221 if (new_host)
222 new_host->HandleNativeWidgetActivationChanged(true);
226 void X11DesktopHandler::OnWindowCreatedOrDestroyed(int event_type,
227 XID window) {
228 // Menus created by Chrome can be drag and drop targets. Since they are
229 // direct children of the screen root window and have override_redirect
230 // we cannot use regular _NET_CLIENT_LIST_STACKING property to find them
231 // and use a separate cache to keep track of them.
232 // TODO(varkha): Implement caching of all top level X windows and their
233 // coordinates and stacking order to eliminate repeated calls to the X server
234 // during mouse movement, drag and shaping events.
235 if (event_type == CreateNotify) {
236 // The window might be destroyed if the message pump did not get a chance to
237 // run but we can safely ignore the X error.
238 gfx::X11ErrorTracker error_tracker;
239 ui::XMenuList::GetInstance()->MaybeRegisterMenu(window);
240 } else {
241 ui::XMenuList::GetInstance()->MaybeUnregisterMenu(window);
244 if (event_type == DestroyNotify) {
245 // Notify the XForeignWindowManager that |window| has been destroyed.
246 ui::XForeignWindowManager::GetInstance()->OnWindowDestroyed(window);
250 } // namespace views