Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(340)

Side by Side Diff: base/message_loop/message_pump_x11.cc

Issue 219743002: x11: Move X event handling out of the message-pump. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: tot-merge-r261267 Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/message_loop/message_pump_x11.h ('k') | base/run_loop.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
OLDNEW
« no previous file with comments | « base/message_loop/message_pump_x11.h ('k') | base/run_loop.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698