OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "base/message_pump_aurax11.h" | |
6 | |
7 #include <glib.h> | |
8 #include <X11/X.h> | |
9 #include <X11/extensions/XInput2.h> | |
10 #include <X11/XKBlib.h> | |
11 | |
12 #include "base/basictypes.h" | |
13 #include "base/message_loop.h" | |
14 | |
15 namespace { | |
16 | |
17 gboolean XSourcePrepare(GSource* source, gint* timeout_ms) { | |
18 if (XPending(base::MessagePumpAuraX11::GetDefaultXDisplay())) | |
19 *timeout_ms = 0; | |
20 else | |
21 *timeout_ms = -1; | |
22 return FALSE; | |
23 } | |
24 | |
25 gboolean XSourceCheck(GSource* source) { | |
26 return XPending(base::MessagePumpAuraX11::GetDefaultXDisplay()); | |
27 } | |
28 | |
29 gboolean XSourceDispatch(GSource* source, | |
30 GSourceFunc unused_func, | |
31 gpointer data) { | |
32 base::MessagePumpAuraX11* pump = static_cast<base::MessagePumpAuraX11*>(data); | |
33 return pump->DispatchXEvents(); | |
34 } | |
35 | |
36 GSourceFuncs XSourceFuncs = { | |
37 XSourcePrepare, | |
38 XSourceCheck, | |
39 XSourceDispatch, | |
40 NULL | |
41 }; | |
42 | |
43 // The connection is essentially a global that's accessed through a static | |
44 // method and destroyed whenever ~MessagePumpAuraX11() is called. We do this | |
45 // for historical reasons so user code can call | |
46 // MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef | |
47 // to whatever type in the current build. | |
48 // | |
49 // TODO(erg): This can be changed to something more sane like | |
50 // MessagePumpAuraX11::Current()->display() once MessagePumpGtk goes away. | |
51 Display* g_xdisplay = NULL; | |
52 int g_xinput_opcode = -1; | |
53 | |
54 bool InitializeXInput2Internal() { | |
55 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); | |
56 if (!display) | |
57 return false; | |
58 | |
59 int event, err; | |
60 | |
61 int xiopcode; | |
62 if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) { | |
63 DVLOG(1) << "X Input extension not available."; | |
64 return false; | |
65 } | |
66 g_xinput_opcode = xiopcode; | |
67 | |
68 #if defined(USE_XI2_MT) | |
69 // USE_XI2_MT also defines the required XI2 minor minimum version. | |
70 int major = 2, minor = USE_XI2_MT; | |
71 #else | |
72 int major = 2, minor = 0; | |
73 #endif | |
74 if (XIQueryVersion(display, &major, &minor) == BadRequest) { | |
75 DVLOG(1) << "XInput2 not supported in the server."; | |
76 return false; | |
77 } | |
78 #if defined(USE_XI2_MT) | |
79 if (major < 2 || (major == 2 && minor < USE_XI2_MT)) { | |
80 DVLOG(1) << "XI version on server is " << major << "." << minor << ". " | |
81 << "But 2." << USE_XI2_MT << " is required."; | |
82 return false; | |
83 } | |
84 #endif | |
85 | |
86 return true; | |
87 } | |
88 | |
89 Window FindEventTarget(const base::NativeEvent& xev) { | |
90 Window target = xev->xany.window; | |
91 if (xev->type == GenericEvent && | |
92 static_cast<XIEvent*>(xev->xcookie.data)->extension == g_xinput_opcode) { | |
93 target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event; | |
94 } | |
95 return target; | |
96 } | |
97 | |
98 bool InitializeXInput2() { | |
99 static bool xinput2_supported = InitializeXInput2Internal(); | |
100 return xinput2_supported; | |
101 } | |
102 | |
103 bool InitializeXkb() { | |
104 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); | |
105 if (!display) | |
106 return false; | |
107 | |
108 int opcode, event, error; | |
109 int major = XkbMajorVersion; | |
110 int minor = XkbMinorVersion; | |
111 if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) { | |
112 DVLOG(1) << "Xkb extension not available."; | |
113 return false; | |
114 } | |
115 | |
116 // Ask the server not to send KeyRelease event when the user holds down a key. | |
117 // crbug.com/138092 | |
118 Bool supported_return; | |
119 if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) { | |
120 DVLOG(1) << "XKB not supported in the server."; | |
121 return false; | |
122 } | |
123 | |
124 return true; | |
125 } | |
126 | |
127 } // namespace | |
128 | |
129 namespace base { | |
130 | |
131 MessagePumpAuraX11::MessagePumpAuraX11() : MessagePumpGlib(), | |
132 x_source_(NULL) { | |
133 InitializeXInput2(); | |
134 InitializeXkb(); | |
135 InitXSource(); | |
136 | |
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); | |
140 } | |
141 | |
142 // static | |
143 Display* MessagePumpAuraX11::GetDefaultXDisplay() { | |
144 if (!g_xdisplay) | |
145 g_xdisplay = XOpenDisplay(NULL); | |
146 return g_xdisplay; | |
147 } | |
148 | |
149 // static | |
150 bool MessagePumpAuraX11::HasXInput2() { | |
151 return InitializeXInput2(); | |
152 } | |
153 | |
154 // static | |
155 MessagePumpAuraX11* MessagePumpAuraX11::Current() { | |
156 MessageLoopForUI* loop = MessageLoopForUI::current(); | |
157 return static_cast<MessagePumpAuraX11*>(loop->pump_ui()); | |
158 } | |
159 | |
160 void MessagePumpAuraX11::AddDispatcherForWindow( | |
161 MessagePumpDispatcher* dispatcher, | |
162 unsigned long xid) { | |
163 dispatchers_.insert(std::make_pair(xid, dispatcher)); | |
164 } | |
165 | |
166 void MessagePumpAuraX11::RemoveDispatcherForWindow(unsigned long xid) { | |
167 dispatchers_.erase(xid); | |
168 } | |
169 | |
170 void MessagePumpAuraX11::AddDispatcherForRootWindow( | |
171 MessagePumpDispatcher* dispatcher) { | |
172 root_window_dispatchers_.AddObserver(dispatcher); | |
173 } | |
174 | |
175 void MessagePumpAuraX11::RemoveDispatcherForRootWindow( | |
176 MessagePumpDispatcher* dispatcher) { | |
177 root_window_dispatchers_.RemoveObserver(dispatcher); | |
178 } | |
179 | |
180 bool MessagePumpAuraX11::DispatchXEvents() { | |
181 Display* display = GetDefaultXDisplay(); | |
182 DCHECK(display); | |
183 MessagePumpDispatcher* dispatcher = | |
184 GetDispatcher() ? GetDispatcher() : this; | |
185 | |
186 // In the general case, we want to handle all pending events before running | |
187 // the tasks. This is what happens in the message_pump_glib case. | |
188 while (XPending(display)) { | |
189 XEvent xev; | |
190 XNextEvent(display, &xev); | |
191 if (dispatcher && ProcessXEvent(dispatcher, &xev)) | |
192 return TRUE; | |
193 } | |
194 return TRUE; | |
195 } | |
196 | |
197 void MessagePumpAuraX11::BlockUntilWindowMapped(unsigned long xid) { | |
198 XEvent event; | |
199 | |
200 Display* display = GetDefaultXDisplay(); | |
201 DCHECK(display); | |
202 | |
203 MessagePumpDispatcher* dispatcher = | |
204 GetDispatcher() ? GetDispatcher() : this; | |
205 | |
206 do { | |
207 // Block until there's a message of |event_mask| type on |w|. Then remove | |
208 // it from the queue and stuff it in |event|. | |
209 XWindowEvent(display, xid, StructureNotifyMask, &event); | |
210 ProcessXEvent(dispatcher, &event); | |
211 } while (event.type != MapNotify); | |
212 } | |
213 | |
214 MessagePumpAuraX11::~MessagePumpAuraX11() { | |
215 g_source_destroy(x_source_); | |
216 g_source_unref(x_source_); | |
217 XCloseDisplay(g_xdisplay); | |
218 g_xdisplay = NULL; | |
219 } | |
220 | |
221 void MessagePumpAuraX11::InitXSource() { | |
222 // CHECKs are to help track down crbug.com/113106. | |
223 CHECK(!x_source_); | |
224 Display* display = GetDefaultXDisplay(); | |
225 CHECK(display) << "Unable to get connection to X server"; | |
226 x_poll_.reset(new GPollFD()); | |
227 CHECK(x_poll_.get()); | |
228 x_poll_->fd = ConnectionNumber(display); | |
229 x_poll_->events = G_IO_IN; | |
230 | |
231 x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource)); | |
232 g_source_add_poll(x_source_, x_poll_.get()); | |
233 g_source_set_can_recurse(x_source_, TRUE); | |
234 g_source_set_callback(x_source_, NULL, this, NULL); | |
235 g_source_attach(x_source_, g_main_context_default()); | |
236 } | |
237 | |
238 bool MessagePumpAuraX11::ProcessXEvent(MessagePumpDispatcher* dispatcher, | |
239 XEvent* xev) { | |
240 bool should_quit = false; | |
241 | |
242 bool have_cookie = false; | |
243 if (xev->type == GenericEvent && | |
244 XGetEventData(xev->xgeneric.display, &xev->xcookie)) { | |
245 have_cookie = true; | |
246 } | |
247 | |
248 if (!WillProcessXEvent(xev)) { | |
249 if (!dispatcher->Dispatch(xev)) { | |
250 should_quit = true; | |
251 Quit(); | |
252 } | |
253 DidProcessXEvent(xev); | |
254 } | |
255 | |
256 if (have_cookie) { | |
257 XFreeEventData(xev->xgeneric.display, &xev->xcookie); | |
258 } | |
259 | |
260 return should_quit; | |
261 } | |
262 | |
263 bool MessagePumpAuraX11::WillProcessXEvent(XEvent* xevent) { | |
264 if (!observers().might_have_observers()) | |
265 return false; | |
266 ObserverListBase<MessagePumpObserver>::Iterator it(observers()); | |
267 MessagePumpObserver* obs; | |
268 while ((obs = it.GetNext()) != NULL) { | |
269 if (obs->WillProcessEvent(xevent)) | |
270 return true; | |
271 } | |
272 return false; | |
273 } | |
274 | |
275 void MessagePumpAuraX11::DidProcessXEvent(XEvent* xevent) { | |
276 FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent)); | |
277 } | |
278 | |
279 MessagePumpDispatcher* MessagePumpAuraX11::GetDispatcherForXEvent( | |
280 const base::NativeEvent& xev) const { | |
281 ::Window x_window = FindEventTarget(xev); | |
282 DispatchersMap::const_iterator it = dispatchers_.find(x_window); | |
283 return it != dispatchers_.end() ? it->second : NULL; | |
284 } | |
285 | |
286 bool MessagePumpAuraX11::Dispatch(const base::NativeEvent& xev) { | |
287 // MappingNotify events (meaning that the keyboard or pointer buttons have | |
288 // been remapped) aren't associated with a window; send them to all | |
289 // dispatchers. | |
290 if (xev->type == MappingNotify) { | |
291 for (DispatchersMap::const_iterator it = dispatchers_.begin(); | |
292 it != dispatchers_.end(); ++it) { | |
293 it->second->Dispatch(xev); | |
294 } | |
295 return true; | |
296 } | |
297 | |
298 if (FindEventTarget(xev) == x_root_window_) { | |
299 FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_, | |
300 Dispatch(xev)); | |
301 return true; | |
302 } | |
303 MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev); | |
304 return dispatcher ? dispatcher->Dispatch(xev) : true; | |
305 } | |
306 | |
307 } // namespace base | |
OLD | NEW |