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 "base/message_loop/message_pump_x11.h"
9 #include <X11/extensions/XInput2.h>
10 #include <X11/XKBlib.h>
12 #include "base/basictypes.h"
13 #include "base/message_loop/message_loop.h"
19 gboolean
XSourcePrepare(GSource
* source
, gint
* timeout_ms
) {
20 if (XPending(MessagePumpX11::GetDefaultXDisplay()))
27 gboolean
XSourceCheck(GSource
* source
) {
28 return XPending(MessagePumpX11::GetDefaultXDisplay());
31 gboolean
XSourceDispatch(GSource
* source
,
32 GSourceFunc unused_func
,
34 MessagePumpX11
* pump
= static_cast<MessagePumpX11
*>(data
);
35 return pump
->DispatchXEvents();
38 GSourceFuncs XSourceFuncs
= {
45 // The connection is essentially a global that's accessed through a static
46 // method and destroyed whenever ~MessagePumpX11() is called. We do this
47 // for historical reasons so user code can call
48 // MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef
49 // to whatever type in the current build.
51 // TODO(erg): This can be changed to something more sane like
52 // MessagePumpX11::Current()->display() once MessagePumpGtk goes away.
53 Display
* g_xdisplay
= NULL
;
54 int g_xinput_opcode
= -1;
56 bool InitializeXInput2Internal() {
57 Display
* display
= MessagePumpX11::GetDefaultXDisplay();
64 if (!XQueryExtension(display
, "XInputExtension", &xiopcode
, &event
, &err
)) {
65 DVLOG(1) << "X Input extension not available.";
68 g_xinput_opcode
= xiopcode
;
70 #if defined(USE_XI2_MT)
71 // USE_XI2_MT also defines the required XI2 minor minimum version.
72 int major
= 2, minor
= USE_XI2_MT
;
74 int major
= 2, minor
= 0;
76 if (XIQueryVersion(display
, &major
, &minor
) == BadRequest
) {
77 DVLOG(1) << "XInput2 not supported in the server.";
80 #if defined(USE_XI2_MT)
81 if (major
< 2 || (major
== 2 && minor
< USE_XI2_MT
)) {
82 DVLOG(1) << "XI version on server is " << major
<< "." << minor
<< ". "
83 << "But 2." << USE_XI2_MT
<< " is required.";
91 Window
FindEventTarget(const NativeEvent
& xev
) {
92 Window target
= xev
->xany
.window
;
93 if (xev
->type
== GenericEvent
&&
94 static_cast<XIEvent
*>(xev
->xcookie
.data
)->extension
== g_xinput_opcode
) {
95 target
= static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
)->event
;
100 bool InitializeXInput2() {
101 static bool xinput2_supported
= InitializeXInput2Internal();
102 return xinput2_supported
;
105 bool InitializeXkb() {
106 Display
* display
= MessagePumpX11::GetDefaultXDisplay();
110 int opcode
, event
, error
;
111 int major
= XkbMajorVersion
;
112 int minor
= XkbMinorVersion
;
113 if (!XkbQueryExtension(display
, &opcode
, &event
, &error
, &major
, &minor
)) {
114 DVLOG(1) << "Xkb extension not available.";
118 // Ask the server not to send KeyRelease event when the user holds down a key.
120 Bool supported_return
;
121 if (!XkbSetDetectableAutoRepeat(display
, True
, &supported_return
)) {
122 DVLOG(1) << "XKB not supported in the server.";
131 MessagePumpX11::MessagePumpX11() : MessagePumpGlib(),
137 // Can't put this in the initializer list because g_xdisplay may not exist
138 // until after InitXSource().
139 x_root_window_
= DefaultRootWindow(g_xdisplay
);
142 MessagePumpX11::~MessagePumpX11() {
143 g_source_destroy(x_source_
);
144 g_source_unref(x_source_
);
145 XCloseDisplay(g_xdisplay
);
150 Display
* MessagePumpX11::GetDefaultXDisplay() {
152 g_xdisplay
= XOpenDisplay(NULL
);
157 bool MessagePumpX11::HasXInput2() {
158 return InitializeXInput2();
161 #if defined(TOOLKIT_GTK)
163 MessagePumpX11
* MessagePumpX11::Current() {
164 MessageLoop
* loop
= MessageLoop::current();
165 return static_cast<MessagePumpX11
*>(loop
->pump_gpu());
169 MessagePumpX11
* MessagePumpX11::Current() {
170 MessageLoopForUI
* loop
= MessageLoopForUI::current();
171 return static_cast<MessagePumpX11
*>(loop
->pump_ui());
175 void MessagePumpX11::AddDispatcherForWindow(
176 MessagePumpDispatcher
* dispatcher
,
178 dispatchers_
.insert(std::make_pair(xid
, dispatcher
));
181 void MessagePumpX11::RemoveDispatcherForWindow(unsigned long xid
) {
182 dispatchers_
.erase(xid
);
185 void MessagePumpX11::AddDispatcherForRootWindow(
186 MessagePumpDispatcher
* dispatcher
) {
187 root_window_dispatchers_
.AddObserver(dispatcher
);
190 void MessagePumpX11::RemoveDispatcherForRootWindow(
191 MessagePumpDispatcher
* dispatcher
) {
192 root_window_dispatchers_
.RemoveObserver(dispatcher
);
195 void MessagePumpX11::AddObserver(MessagePumpObserver
* observer
) {
196 observers_
.AddObserver(observer
);
199 void MessagePumpX11::RemoveObserver(MessagePumpObserver
* observer
) {
200 observers_
.RemoveObserver(observer
);
203 bool MessagePumpX11::DispatchXEvents() {
204 Display
* display
= GetDefaultXDisplay();
206 MessagePumpDispatcher
* dispatcher
=
207 GetDispatcher() ? GetDispatcher() : this;
209 // In the general case, we want to handle all pending events before running
210 // the tasks. This is what happens in the message_pump_glib case.
211 while (XPending(display
)) {
213 XNextEvent(display
, &xev
);
214 if (dispatcher
&& ProcessXEvent(dispatcher
, &xev
))
220 void MessagePumpX11::BlockUntilWindowMapped(unsigned long xid
) {
223 Display
* display
= GetDefaultXDisplay();
226 MessagePumpDispatcher
* dispatcher
=
227 GetDispatcher() ? GetDispatcher() : this;
230 // Block until there's a message of |event_mask| type on |w|. Then remove
231 // it from the queue and stuff it in |event|.
232 XWindowEvent(display
, xid
, StructureNotifyMask
, &event
);
233 ProcessXEvent(dispatcher
, &event
);
234 } while (event
.type
!= MapNotify
);
237 void MessagePumpX11::InitXSource() {
238 // CHECKs are to help track down crbug.com/113106.
240 Display
* display
= GetDefaultXDisplay();
241 CHECK(display
) << "Unable to get connection to X server";
242 x_poll_
.reset(new GPollFD());
243 CHECK(x_poll_
.get());
244 x_poll_
->fd
= ConnectionNumber(display
);
245 x_poll_
->events
= G_IO_IN
;
247 x_source_
= g_source_new(&XSourceFuncs
, sizeof(GSource
));
248 g_source_add_poll(x_source_
, x_poll_
.get());
249 g_source_set_can_recurse(x_source_
, TRUE
);
250 g_source_set_callback(x_source_
, NULL
, this, NULL
);
251 g_source_attach(x_source_
, g_main_context_default());
254 bool MessagePumpX11::ProcessXEvent(MessagePumpDispatcher
* dispatcher
,
256 bool should_quit
= false;
258 bool have_cookie
= false;
259 if (xev
->type
== GenericEvent
&&
260 XGetEventData(xev
->xgeneric
.display
, &xev
->xcookie
)) {
264 if (!WillProcessXEvent(xev
)) {
265 if (!dispatcher
->Dispatch(xev
)) {
269 DidProcessXEvent(xev
);
273 XFreeEventData(xev
->xgeneric
.display
, &xev
->xcookie
);
279 bool MessagePumpX11::WillProcessXEvent(XEvent
* xevent
) {
280 if (!observers().might_have_observers())
282 ObserverListBase
<MessagePumpObserver
>::Iterator
it(observers());
283 MessagePumpObserver
* obs
;
284 while ((obs
= it
.GetNext()) != NULL
) {
285 if (obs
->WillProcessEvent(xevent
))
291 void MessagePumpX11::DidProcessXEvent(XEvent
* xevent
) {
292 FOR_EACH_OBSERVER(MessagePumpObserver
, observers(), DidProcessEvent(xevent
));
295 MessagePumpDispatcher
* MessagePumpX11::GetDispatcherForXEvent(
296 const NativeEvent
& xev
) const {
297 ::Window x_window
= FindEventTarget(xev
);
298 DispatchersMap::const_iterator it
= dispatchers_
.find(x_window
);
299 return it
!= dispatchers_
.end() ? it
->second
: NULL
;
302 bool MessagePumpX11::Dispatch(const NativeEvent
& xev
) {
303 // MappingNotify events (meaning that the keyboard or pointer buttons have
304 // been remapped) aren't associated with a window; send them to all
306 if (xev
->type
== MappingNotify
) {
307 for (DispatchersMap::const_iterator it
= dispatchers_
.begin();
308 it
!= dispatchers_
.end(); ++it
) {
309 it
->second
->Dispatch(xev
);
314 if (FindEventTarget(xev
) == x_root_window_
) {
315 FOR_EACH_OBSERVER(MessagePumpDispatcher
, root_window_dispatchers_
,
319 MessagePumpDispatcher
* dispatcher
= GetDispatcherForXEvent(xev
);
320 return dispatcher
? dispatcher
->Dispatch(xev
) : true;