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/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/widget/desktop_aura/desktop_window_tree_host_x11.h"
22 const char* kAtomsToCache
[] = {
27 // Our global instance. Deleted when our Env() is deleted.
28 views::X11DesktopHandler
* g_handler
= NULL
;
35 X11DesktopHandler
* X11DesktopHandler::get() {
37 g_handler
= new X11DesktopHandler
;
42 X11DesktopHandler::X11DesktopHandler()
43 : xdisplay_(gfx::GetXDisplay()),
44 x_root_window_(DefaultRootWindow(xdisplay_
)),
45 x_active_window_(None
),
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;
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 if (wm_supports_active_window_
) {
90 DCHECK_EQ(gfx::GetXDisplay(), xdisplay_
);
92 // If the window is not already active, send a hint to activate it
93 if (x_active_window_
!= window
) {
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
,
110 OnActiveWindowChanged(window
, ACTIVE
);
113 XRaiseWindow(xdisplay_
, window
);
114 // Directly ask the X server to give focus to the window. Note
115 // that the call will raise an X error if the window is not
117 XSetInputFocus(xdisplay_
, window
, RevertToParent
, CurrentTime
);
119 OnActiveWindowChanged(window
, ACTIVE
);
123 void X11DesktopHandler::DeactivateWindow(::Window window
) {
124 if (!IsActiveWindow(window
))
127 XLowerWindow(xdisplay_
, window
);
129 // Per ICCCM: http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
130 // "Clients should not give up the input focus of their own volition.
131 // They should ignore input that they receive instead."
133 // There is nothing else that we can do. Pretend that we have been
134 // deactivated and ignore keyboard input in DesktopWindowTreeHostX11.
135 OnActiveWindowChanged(window
, NOT_ACTIVE
);
138 bool X11DesktopHandler::IsActiveWindow(::Window window
) const {
139 return window
== current_window_
&& current_window_active_state_
== ACTIVE
;
142 void X11DesktopHandler::ProcessXEvent(XEvent
* event
) {
143 // Ignore focus events that are being sent only because the pointer is over
144 // our window, even if the input focus is in a different window.
145 if (event
->xfocus
.detail
== NotifyPointer
)
148 switch (event
->type
) {
150 if (current_window_
!= event
->xfocus
.window
)
151 OnActiveWindowChanged(event
->xfocus
.window
, ACTIVE
);
154 if (current_window_
== event
->xfocus
.window
)
155 OnActiveWindowChanged(None
, NOT_ACTIVE
);
162 bool X11DesktopHandler::CanDispatchEvent(const ui::PlatformEvent
& event
) {
163 return event
->type
== CreateNotify
|| event
->type
== DestroyNotify
||
164 (event
->type
== PropertyNotify
&&
165 event
->xproperty
.window
== x_root_window_
);
168 uint32_t X11DesktopHandler::DispatchEvent(const ui::PlatformEvent
& event
) {
169 switch (event
->type
) {
170 case PropertyNotify
: {
171 // Check for a change to the active window.
172 CHECK_EQ(x_root_window_
, event
->xproperty
.window
);
173 ::Atom active_window_atom
= atom_cache_
.GetAtom("_NET_ACTIVE_WINDOW");
174 if (event
->xproperty
.atom
== active_window_atom
) {
176 if (ui::GetXIDProperty(x_root_window_
, "_NET_ACTIVE_WINDOW", &window
) &&
178 x_active_window_
= window
;
179 OnActiveWindowChanged(window
, ACTIVE
);
181 x_active_window_
= None
;
188 OnWindowCreatedOrDestroyed(event
->type
, event
->xcreatewindow
.window
);
191 OnWindowCreatedOrDestroyed(event
->type
, event
->xdestroywindow
.window
);
192 // If the current active window is being destroyed, reset our tracker.
193 if (x_active_window_
== event
->xdestroywindow
.window
) {
194 x_active_window_
= None
;
201 return ui::POST_DISPATCH_NONE
;
204 void X11DesktopHandler::OnWindowInitialized(aura::Window
* window
) {
207 void X11DesktopHandler::OnWillDestroyEnv() {
212 void X11DesktopHandler::OnActiveWindowChanged(::Window xid
,
213 ActiveState active_state
) {
214 if (current_window_
== xid
&& current_window_active_state_
== active_state
)
217 if (current_window_active_state_
== ACTIVE
) {
218 DesktopWindowTreeHostX11
* old_host
=
219 views::DesktopWindowTreeHostX11::GetHostForXID(current_window_
);
221 old_host
->HandleNativeWidgetActivationChanged(false);
224 // Update the current window ID to effectively change the active widget.
225 current_window_
= xid
;
226 current_window_active_state_
= active_state
;
228 if (active_state
== ACTIVE
) {
229 DesktopWindowTreeHostX11
* new_host
=
230 views::DesktopWindowTreeHostX11::GetHostForXID(xid
);
232 new_host
->HandleNativeWidgetActivationChanged(true);
236 void X11DesktopHandler::OnWindowCreatedOrDestroyed(int event_type
,
238 // Menus created by Chrome can be drag and drop targets. Since they are
239 // direct children of the screen root window and have override_redirect
240 // we cannot use regular _NET_CLIENT_LIST_STACKING property to find them
241 // and use a separate cache to keep track of them.
242 // TODO(varkha): Implement caching of all top level X windows and their
243 // coordinates and stacking order to eliminate repeated calls to the X server
244 // during mouse movement, drag and shaping events.
245 if (event_type
== CreateNotify
) {
246 // The window might be destroyed if the message pump did not get a chance to
247 // run but we can safely ignore the X error.
248 gfx::X11ErrorTracker error_tracker
;
249 ui::XMenuList::GetInstance()->MaybeRegisterMenu(window
);
251 ui::XMenuList::GetInstance()->MaybeUnregisterMenu(window
);
254 if (event_type
== DestroyNotify
) {
255 // Notify the XForeignWindowManager that |window| has been destroyed.
256 ui::XForeignWindowManager::GetInstance()->OnWindowDestroyed(window
);