OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/message_loop/message_pump_x11.h" | 5 #include "base/message_loop/message_pump_x11.h" |
6 | 6 |
7 #include <glib.h> | 7 #include <glib.h> |
8 #include <X11/X.h> | 8 #include <X11/X.h> |
9 #include <X11/extensions/XInput2.h> | 9 #include <X11/extensions/XInput2.h> |
10 #include <X11/XKBlib.h> | 10 #include <X11/XKBlib.h> |
11 | 11 |
12 #include "base/basictypes.h" | 12 #include "base/basictypes.h" |
13 #include "base/message_loop/message_loop.h" | 13 #include "base/message_loop/message_loop.h" |
14 | 14 |
15 namespace base { | 15 namespace base { |
16 | 16 |
17 namespace { | 17 namespace { |
18 | 18 |
19 gboolean XSourcePrepare(GSource* source, gint* timeout_ms) { | |
20 if (XPending(MessagePumpX11::GetDefaultXDisplay())) | |
21 *timeout_ms = 0; | |
22 else | |
23 *timeout_ms = -1; | |
24 return FALSE; | |
25 } | |
26 | |
27 gboolean XSourceCheck(GSource* source) { | |
28 return XPending(MessagePumpX11::GetDefaultXDisplay()); | |
29 } | |
30 | |
31 gboolean XSourceDispatch(GSource* source, | |
32 GSourceFunc unused_func, | |
33 gpointer data) { | |
34 MessagePumpX11* pump = static_cast<MessagePumpX11*>(data); | |
35 pump->DispatchXEvents(); | |
36 return TRUE; | |
37 } | |
38 | |
39 GSourceFuncs XSourceFuncs = { | |
40 XSourcePrepare, | |
41 XSourceCheck, | |
42 XSourceDispatch, | |
43 NULL | |
44 }; | |
45 | |
46 // The connection is essentially a global that's accessed through a static | 19 // The connection is essentially a global that's accessed through a static |
47 // method and destroyed whenever ~MessagePumpX11() is called. We do this | 20 // method and destroyed whenever ~MessagePumpX11() is called. We do this |
48 // for historical reasons so user code can call | 21 // for historical reasons so user code can call |
49 // MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef | 22 // MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef |
50 // to whatever type in the current build. | 23 // to whatever type in the current build. |
51 // | 24 // |
52 // TODO(erg): This can be changed to something more sane like | 25 // TODO(erg): This can be changed to something more sane like |
53 // MessagePumpX11::Current()->display() once MessagePumpGtk goes away. | 26 // MessagePumpX11::Current()->display() once MessagePumpGtk goes away. |
54 Display* g_xdisplay = NULL; | 27 Display* g_xdisplay = NULL; |
55 int g_xinput_opcode = -1; | |
56 | |
57 bool InitializeXInput2() { | |
58 Display* display = MessagePumpX11::GetDefaultXDisplay(); | |
59 if (!display) | |
60 return false; | |
61 | |
62 int event, err; | |
63 | |
64 int xiopcode; | |
65 if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) { | |
66 DVLOG(1) << "X Input extension not available."; | |
67 return false; | |
68 } | |
69 g_xinput_opcode = xiopcode; | |
70 | |
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; | |
74 #else | |
75 int major = 2, minor = 0; | |
76 #endif | |
77 if (XIQueryVersion(display, &major, &minor) == BadRequest) { | |
78 DVLOG(1) << "XInput2 not supported in the server."; | |
79 return false; | |
80 } | |
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."; | |
85 return false; | |
86 } | |
87 #endif | |
88 | |
89 return true; | |
90 } | |
91 | |
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; | |
97 } | |
98 return target; | |
99 } | |
100 | |
101 bool InitializeXkb() { | |
102 Display* display = MessagePumpX11::GetDefaultXDisplay(); | |
103 if (!display) | |
104 return false; | |
105 | |
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."; | |
111 return false; | |
112 } | |
113 | |
114 // Ask the server not to send KeyRelease event when the user holds down a key. | |
115 // crbug.com/138092 | |
116 Bool supported_return; | |
117 if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) { | |
118 DVLOG(1) << "XKB not supported in the server."; | |
119 return false; | |
120 } | |
121 | |
122 return true; | |
123 } | |
124 | 28 |
125 } // namespace | 29 } // namespace |
126 | 30 |
127 MessagePumpX11::MessagePumpX11() : MessagePumpGlib(), | 31 MessagePumpX11::MessagePumpX11() : MessagePumpGlib() {} |
128 x_source_(NULL) { | |
129 InitializeXInput2(); | |
130 InitializeXkb(); | |
131 InitXSource(); | |
132 | |
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); | |
136 } | |
137 | 32 |
138 MessagePumpX11::~MessagePumpX11() { | 33 MessagePumpX11::~MessagePumpX11() { |
139 g_source_destroy(x_source_); | 34 if (g_xdisplay) { |
140 g_source_unref(x_source_); | 35 XCloseDisplay(g_xdisplay); |
141 XCloseDisplay(g_xdisplay); | 36 g_xdisplay = NULL; |
142 g_xdisplay = NULL; | 37 } |
143 } | 38 } |
144 | 39 |
145 // static | 40 // static |
146 Display* MessagePumpX11::GetDefaultXDisplay() { | 41 Display* MessagePumpX11::GetDefaultXDisplay() { |
147 if (!g_xdisplay) | 42 if (!g_xdisplay) |
148 g_xdisplay = XOpenDisplay(NULL); | 43 g_xdisplay = XOpenDisplay(NULL); |
149 return g_xdisplay; | 44 return g_xdisplay; |
150 } | 45 } |
151 | 46 |
152 #if defined(TOOLKIT_GTK) | 47 #if defined(TOOLKIT_GTK) |
153 // static | 48 // static |
154 MessagePumpX11* MessagePumpX11::Current() { | 49 MessagePumpX11* MessagePumpX11::Current() { |
155 MessageLoop* loop = MessageLoop::current(); | 50 MessageLoop* loop = MessageLoop::current(); |
156 return static_cast<MessagePumpX11*>(loop->pump_gpu()); | 51 return static_cast<MessagePumpX11*>(loop->pump_gpu()); |
157 } | 52 } |
158 #else | 53 #else |
159 // static | 54 // static |
160 MessagePumpX11* MessagePumpX11::Current() { | 55 MessagePumpX11* MessagePumpX11::Current() { |
161 MessageLoopForUI* loop = MessageLoopForUI::current(); | 56 MessageLoopForUI* loop = MessageLoopForUI::current(); |
162 return static_cast<MessagePumpX11*>(loop->pump_ui()); | 57 return static_cast<MessagePumpX11*>(loop->pump_ui()); |
163 } | 58 } |
164 #endif | 59 #endif |
165 | 60 |
166 void MessagePumpX11::AddDispatcherForWindow( | |
167 MessagePumpDispatcher* dispatcher, | |
168 unsigned long xid) { | |
169 dispatchers_.insert(std::make_pair(xid, dispatcher)); | |
170 } | |
171 | |
172 void MessagePumpX11::RemoveDispatcherForWindow(unsigned long xid) { | |
173 dispatchers_.erase(xid); | |
174 } | |
175 | |
176 void MessagePumpX11::AddDispatcherForRootWindow( | |
177 MessagePumpDispatcher* dispatcher) { | |
178 root_window_dispatchers_.AddObserver(dispatcher); | |
179 } | |
180 | |
181 void MessagePumpX11::RemoveDispatcherForRootWindow( | |
182 MessagePumpDispatcher* dispatcher) { | |
183 root_window_dispatchers_.RemoveObserver(dispatcher); | |
184 } | |
185 | |
186 void MessagePumpX11::AddObserver(MessagePumpObserver* observer) { | 61 void MessagePumpX11::AddObserver(MessagePumpObserver* observer) { |
187 observers_.AddObserver(observer); | 62 observers_.AddObserver(observer); |
188 } | 63 } |
189 | 64 |
190 void MessagePumpX11::RemoveObserver(MessagePumpObserver* observer) { | 65 void MessagePumpX11::RemoveObserver(MessagePumpObserver* observer) { |
191 observers_.RemoveObserver(observer); | 66 observers_.RemoveObserver(observer); |
192 } | 67 } |
193 | 68 |
194 void MessagePumpX11::DispatchXEvents() { | |
195 Display* display = GetDefaultXDisplay(); | |
196 DCHECK(display); | |
197 MessagePumpDispatcher* dispatcher = GetDispatcher(); | |
198 if (!dispatcher) | |
199 dispatcher = this; | |
200 | |
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)) { | |
204 XEvent xev; | |
205 XNextEvent(display, &xev); | |
206 ProcessXEvent(dispatcher, &xev); | |
207 if (ShouldQuit()) | |
208 break; | |
209 } | |
210 } | |
211 | |
212 void MessagePumpX11::BlockUntilWindowMapped(unsigned long xid) { | |
213 XEvent event; | |
214 | |
215 Display* display = GetDefaultXDisplay(); | |
216 DCHECK(display); | |
217 | |
218 MessagePumpDispatcher* dispatcher = GetDispatcher(); | |
219 if (!dispatcher) | |
220 dispatcher = this; | |
221 | |
222 do { | |
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); | |
228 } | |
229 | |
230 void MessagePumpX11::InitXSource() { | |
231 // CHECKs are to help track down crbug.com/113106. | |
232 CHECK(!x_source_); | |
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; | |
239 | |
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()); | |
245 } | |
246 | |
247 void MessagePumpX11::ProcessXEvent(MessagePumpDispatcher* dispatcher, | |
248 XEvent* xev) { | |
249 CHECK(dispatcher); | |
250 bool have_cookie = false; | |
251 if (xev->type == GenericEvent && | |
252 XGetEventData(xev->xgeneric.display, &xev->xcookie)) { | |
253 have_cookie = true; | |
254 } | |
255 | |
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) | |
262 Quit(); | |
263 DidProcessXEvent(xev); | |
264 } | |
265 | |
266 if (have_cookie) | |
267 XFreeEventData(xev->xgeneric.display, &xev->xcookie); | |
268 } | |
269 | |
270 bool MessagePumpX11::WillProcessXEvent(XEvent* xevent) { | 69 bool MessagePumpX11::WillProcessXEvent(XEvent* xevent) { |
271 if (!observers().might_have_observers()) | 70 if (!observers_.might_have_observers()) |
272 return false; | 71 return false; |
273 ObserverListBase<MessagePumpObserver>::Iterator it(observers()); | 72 ObserverListBase<MessagePumpObserver>::Iterator it(observers_); |
274 MessagePumpObserver* obs; | 73 MessagePumpObserver* obs; |
275 while ((obs = it.GetNext()) != NULL) { | 74 while ((obs = it.GetNext()) != NULL) { |
276 if (obs->WillProcessEvent(xevent)) | 75 if (obs->WillProcessEvent(xevent)) |
277 return true; | 76 return true; |
278 } | 77 } |
279 return false; | 78 return false; |
280 } | 79 } |
281 | 80 |
282 void MessagePumpX11::DidProcessXEvent(XEvent* xevent) { | 81 void MessagePumpX11::DidProcessXEvent(XEvent* xevent) { |
283 FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent)); | 82 FOR_EACH_OBSERVER(MessagePumpObserver, observers_, DidProcessEvent(xevent)); |
284 } | |
285 | |
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; | |
291 } | |
292 | |
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 | |
296 // dispatchers. | |
297 if (xev->type == MappingNotify) { | |
298 for (DispatchersMap::const_iterator it = dispatchers_.begin(); | |
299 it != dispatchers_.end(); ++it) { | |
300 it->second->Dispatch(xev); | |
301 } | |
302 return POST_DISPATCH_NONE; | |
303 } | |
304 | |
305 if (FindEventTarget(xev) == x_root_window_) { | |
306 FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_, | |
307 Dispatch(xev)); | |
308 return POST_DISPATCH_NONE; | |
309 } | |
310 MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev); | |
311 return dispatcher ? dispatcher->Dispatch(xev) : POST_DISPATCH_NONE; | |
312 } | 83 } |
313 | 84 |
314 } // namespace base | 85 } // namespace base |
OLD | NEW |