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 pump
->DispatchXEvents();
39 GSourceFuncs XSourceFuncs
= {
46 // The connection is essentially a global that's accessed through a static
47 // method and destroyed whenever ~MessagePumpX11() is called. We do this
48 // for historical reasons so user code can call
49 // MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef
50 // to whatever type in the current build.
52 // TODO(erg): This can be changed to something more sane like
53 // MessagePumpX11::Current()->display() once MessagePumpGtk goes away.
54 Display
* g_xdisplay
= NULL
;
55 int g_xinput_opcode
= -1;
57 bool InitializeXInput2() {
58 Display
* display
= MessagePumpX11::GetDefaultXDisplay();
65 if (!XQueryExtension(display
, "XInputExtension", &xiopcode
, &event
, &err
)) {
66 DVLOG(1) << "X Input extension not available.";
69 g_xinput_opcode
= xiopcode
;
71 #if defined(USE_XI2_MT)
72 // USE_XI2_MT also defines the required XI2 minor minimum version.
73 int major
= 2, minor
= USE_XI2_MT
;
75 int major
= 2, minor
= 0;
77 if (XIQueryVersion(display
, &major
, &minor
) == BadRequest
) {
78 DVLOG(1) << "XInput2 not supported in the server.";
81 #if defined(USE_XI2_MT)
82 if (major
< 2 || (major
== 2 && minor
< USE_XI2_MT
)) {
83 DVLOG(1) << "XI version on server is " << major
<< "." << minor
<< ". "
84 << "But 2." << USE_XI2_MT
<< " is required.";
92 Window
FindEventTarget(const NativeEvent
& xev
) {
93 Window target
= xev
->xany
.window
;
94 if (xev
->type
== GenericEvent
&&
95 static_cast<XIEvent
*>(xev
->xcookie
.data
)->extension
== g_xinput_opcode
) {
96 target
= static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
)->event
;
101 bool InitializeXkb() {
102 Display
* display
= MessagePumpX11::GetDefaultXDisplay();
106 int opcode
, event
, error
;
107 int major
= XkbMajorVersion
;
108 int minor
= XkbMinorVersion
;
109 if (!XkbQueryExtension(display
, &opcode
, &event
, &error
, &major
, &minor
)) {
110 DVLOG(1) << "Xkb extension not available.";
114 // Ask the server not to send KeyRelease event when the user holds down a key.
116 Bool supported_return
;
117 if (!XkbSetDetectableAutoRepeat(display
, True
, &supported_return
)) {
118 DVLOG(1) << "XKB not supported in the server.";
127 MessagePumpX11::MessagePumpX11() : MessagePumpGlib(),
133 // Can't put this in the initializer list because g_xdisplay may not exist
134 // until after InitXSource().
135 x_root_window_
= DefaultRootWindow(g_xdisplay
);
138 MessagePumpX11::~MessagePumpX11() {
139 g_source_destroy(x_source_
);
140 g_source_unref(x_source_
);
141 XCloseDisplay(g_xdisplay
);
146 Display
* MessagePumpX11::GetDefaultXDisplay() {
148 g_xdisplay
= XOpenDisplay(NULL
);
152 #if defined(TOOLKIT_GTK)
154 MessagePumpX11
* MessagePumpX11::Current() {
155 MessageLoop
* loop
= MessageLoop::current();
156 return static_cast<MessagePumpX11
*>(loop
->pump_gpu());
160 MessagePumpX11
* MessagePumpX11::Current() {
161 MessageLoopForUI
* loop
= MessageLoopForUI::current();
162 return static_cast<MessagePumpX11
*>(loop
->pump_ui());
166 void MessagePumpX11::AddDispatcherForWindow(
167 MessagePumpDispatcher
* dispatcher
,
169 dispatchers_
.insert(std::make_pair(xid
, dispatcher
));
172 void MessagePumpX11::RemoveDispatcherForWindow(unsigned long xid
) {
173 dispatchers_
.erase(xid
);
176 void MessagePumpX11::AddDispatcherForRootWindow(
177 MessagePumpDispatcher
* dispatcher
) {
178 root_window_dispatchers_
.AddObserver(dispatcher
);
181 void MessagePumpX11::RemoveDispatcherForRootWindow(
182 MessagePumpDispatcher
* dispatcher
) {
183 root_window_dispatchers_
.RemoveObserver(dispatcher
);
186 void MessagePumpX11::AddObserver(MessagePumpObserver
* observer
) {
187 observers_
.AddObserver(observer
);
190 void MessagePumpX11::RemoveObserver(MessagePumpObserver
* observer
) {
191 observers_
.RemoveObserver(observer
);
194 void MessagePumpX11::DispatchXEvents() {
195 Display
* display
= GetDefaultXDisplay();
197 MessagePumpDispatcher
* dispatcher
= GetDispatcher();
201 // In the general case, we want to handle all pending events before running
202 // the tasks. This is what happens in the message_pump_glib case.
203 while (XPending(display
)) {
205 XNextEvent(display
, &xev
);
206 ProcessXEvent(dispatcher
, &xev
);
212 void MessagePumpX11::BlockUntilWindowMapped(unsigned long xid
) {
215 Display
* display
= GetDefaultXDisplay();
218 MessagePumpDispatcher
* dispatcher
= GetDispatcher();
223 // Block until there's a message of |event_mask| type on |w|. Then remove
224 // it from the queue and stuff it in |event|.
225 XWindowEvent(display
, xid
, StructureNotifyMask
, &event
);
226 ProcessXEvent(dispatcher
, &event
);
227 } while (event
.type
!= MapNotify
);
230 void MessagePumpX11::InitXSource() {
231 // CHECKs are to help track down crbug.com/113106.
233 Display
* display
= GetDefaultXDisplay();
234 CHECK(display
) << "Unable to get connection to X server";
235 x_poll_
.reset(new GPollFD());
236 CHECK(x_poll_
.get());
237 x_poll_
->fd
= ConnectionNumber(display
);
238 x_poll_
->events
= G_IO_IN
;
240 x_source_
= g_source_new(&XSourceFuncs
, sizeof(GSource
));
241 g_source_add_poll(x_source_
, x_poll_
.get());
242 g_source_set_can_recurse(x_source_
, TRUE
);
243 g_source_set_callback(x_source_
, NULL
, this, NULL
);
244 g_source_attach(x_source_
, g_main_context_default());
247 void MessagePumpX11::ProcessXEvent(MessagePumpDispatcher
* dispatcher
,
250 bool have_cookie
= false;
251 if (xev
->type
== GenericEvent
&&
252 XGetEventData(xev
->xgeneric
.display
, &xev
->xcookie
)) {
256 if (!WillProcessXEvent(xev
)) {
257 uint32_t action
= dispatcher
->Dispatch(xev
);
258 bool should_quit
= (action
& POST_DISPATCH_QUIT_LOOP
);
259 if (dispatcher
!= this && (action
& POST_DISPATCH_PERFORM_DEFAULT
))
260 action
= Dispatch(xev
);
261 if ((action
& POST_DISPATCH_QUIT_LOOP
) || should_quit
)
263 DidProcessXEvent(xev
);
267 XFreeEventData(xev
->xgeneric
.display
, &xev
->xcookie
);
270 bool MessagePumpX11::WillProcessXEvent(XEvent
* xevent
) {
271 if (!observers().might_have_observers())
273 ObserverListBase
<MessagePumpObserver
>::Iterator
it(observers());
274 MessagePumpObserver
* obs
;
275 while ((obs
= it
.GetNext()) != NULL
) {
276 if (obs
->WillProcessEvent(xevent
))
282 void MessagePumpX11::DidProcessXEvent(XEvent
* xevent
) {
283 FOR_EACH_OBSERVER(MessagePumpObserver
, observers(), DidProcessEvent(xevent
));
286 MessagePumpDispatcher
* MessagePumpX11::GetDispatcherForXEvent(
287 const NativeEvent
& xev
) const {
288 ::Window x_window
= FindEventTarget(xev
);
289 DispatchersMap::const_iterator it
= dispatchers_
.find(x_window
);
290 return it
!= dispatchers_
.end() ? it
->second
: NULL
;
293 uint32_t MessagePumpX11::Dispatch(const NativeEvent
& xev
) {
294 // MappingNotify events (meaning that the keyboard or pointer buttons have
295 // been remapped) aren't associated with a window; send them to all
297 if (xev
->type
== MappingNotify
) {
298 for (DispatchersMap::const_iterator it
= dispatchers_
.begin();
299 it
!= dispatchers_
.end(); ++it
) {
300 it
->second
->Dispatch(xev
);
302 return POST_DISPATCH_NONE
;
305 if (FindEventTarget(xev
) == x_root_window_
) {
306 FOR_EACH_OBSERVER(MessagePumpDispatcher
, root_window_dispatchers_
,
308 return POST_DISPATCH_NONE
;
310 MessagePumpDispatcher
* dispatcher
= GetDispatcherForXEvent(xev
);
311 return dispatcher
? dispatcher
->Dispatch(xev
) : POST_DISPATCH_NONE
;